diff options
Diffstat (limited to 'src/pulsecore')
145 files changed, 27456 insertions, 0 deletions
diff --git a/src/pulsecore/Makefile b/src/pulsecore/Makefile new file mode 120000 index 00000000..c110232d --- /dev/null +++ b/src/pulsecore/Makefile @@ -0,0 +1 @@ +../pulse/Makefile
\ No newline at end of file diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c new file mode 100644 index 00000000..7eda1e49 --- /dev/null +++ b/src/pulsecore/authkey-prop.c @@ -0,0 +1,89 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <assert.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/props.h> +#include <pulsecore/log.h> + +#include "authkey-prop.h" + +struct authkey_data { + int ref; + size_t length; +}; + +int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) { + struct authkey_data *a; + assert(c && name && data && len > 0); + + if (!(a = pa_property_get(c, name))) + return -1; + + assert(a->length == len); + memcpy(data, a+1, len); + return 0; +} + +int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) { + struct authkey_data *a; + assert(c && name); + + if (pa_property_get(c, name)) + return -1; + + a = pa_xmalloc(sizeof(struct authkey_data) + len); + a->ref = 1; + a->length = len; + memcpy(a+1, data, len); + + pa_property_set(c, name, a); + + return 0; +} + +void pa_authkey_prop_ref(pa_core *c, const char *name) { + struct authkey_data *a; + assert(c && name); + + a = pa_property_get(c, name); + assert(a && a->ref >= 1); + + a->ref++; +} + +void pa_authkey_prop_unref(pa_core *c, const char *name) { + struct authkey_data *a; + assert(c && name); + + a = pa_property_get(c, name); + assert(a && a->ref >= 1); + + if (!(--a->ref)) { + pa_property_remove(c, name); + pa_xfree(a); + } +} + + diff --git a/src/pulsecore/authkey-prop.h b/src/pulsecore/authkey-prop.h new file mode 100644 index 00000000..b1da28be --- /dev/null +++ b/src/pulsecore/authkey-prop.h @@ -0,0 +1,43 @@ +#ifndef fooauthkeyprophfoo +#define fooauthkeyprophfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/core.h> + +/* The authkey-prop uses a central property to store a previously + * loaded cookie in memory. Useful for sharing the same cookie between + * several modules. */ + +/* Return the data of the specified authorization key property. Doesn't alter the refernce count of the key */ +int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len); + +/* Store data in the specified authorization key property. The initial reference count is set to 1 */ +int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len); + +/* Increase the reference count of the specified authorization key */ +void pa_authkey_prop_ref(pa_core *c, const char *name); + +/* Decrease the reference count of the specified authorization key */ +void pa_authkey_prop_unref(pa_core *c, const char *name); + +#endif diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c new file mode 100644 index 00000000..064209d9 --- /dev/null +++ b/src/pulsecore/authkey.c @@ -0,0 +1,209 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <inttypes.h> +#include <stdlib.h> +#include <time.h> +#include <limits.h> +#include <sys/stat.h> + +#include <pulse/util.h> +#include <pulsecore/core-error.h> +#include <pulsecore/core-util.h> +#include <pulsecore/log.h> +#include <pulsecore/random.h> + +#include "authkey.h" + +/* Generate a new authorization key, store it in file fd and return it in *data */ +static int generate(int fd, void *ret_data, size_t length) { + ssize_t r; + assert(fd >= 0 && ret_data && length); + + pa_random(ret_data, length); + + lseek(fd, 0, SEEK_SET); + ftruncate(fd, 0); + + if ((r = pa_loop_write(fd, ret_data, length)) < 0 || (size_t) r != length) { + pa_log(__FILE__": failed to write cookie file: %s", pa_cstrerror(errno)); + return -1; + } + + return 0; +} + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* Load an euthorization cookie from file fn and store it in data. If + * the cookie file doesn't exist, create it */ +static int load(const char *fn, void *data, size_t length) { + int fd = -1; + int writable = 1; + int unlock = 0, ret = -1; + ssize_t r; + assert(fn && data && length); + + if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR)) < 0) { + if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) { + pa_log(__FILE__": failed to open cookie file '%s': %s", fn, pa_cstrerror(errno)); + goto finish; + } else + writable = 0; + } + + unlock = pa_lock_fd(fd, 1) >= 0; + + if ((r = pa_loop_read(fd, data, length)) < 0) { + pa_log(__FILE__": failed to read cookie file '%s': %s", fn, pa_cstrerror(errno)); + goto finish; + } + + if ((size_t) r != length) { + pa_log_debug(__FILE__": got %d bytes from cookie file '%s', expected %d", (int)r, fn, (int)length); + + if (!writable) { + pa_log(__FILE__": unable to write cookie to read only file"); + goto finish; + } + + if (generate(fd, data, length) < 0) + goto finish; + } + + ret = 0; + +finish: + + if (fd >= 0) { + + if (unlock) + pa_lock_fd(fd, 0); + + close(fd); + } + + return ret; +} + +/* Load a cookie from a cookie file. If the file doesn't exist, create it. */ +int pa_authkey_load(const char *path, void *data, size_t length) { + int ret; + + assert(path && data && length); + + ret = load(path, data, length); + + if (ret < 0) + pa_log(__FILE__": Failed to load authorization key '%s': %s", path, + (ret == -1) ? pa_cstrerror(errno) : "file corrupt"); + + return ret; +} + +/* If the specified file path starts with / return it, otherwise + * return path prepended with home directory */ +static const char *normalize_path(const char *fn, char *s, size_t l) { + assert(fn && s && l > 0); + +#ifndef OS_IS_WIN32 + if (fn[0] != '/') { +#else + if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') { +#endif + char homedir[PATH_MAX]; + if (!pa_get_home_dir(homedir, sizeof(homedir))) + return NULL; + +#ifndef OS_IS_WIN32 + snprintf(s, l, "%s/%s", homedir, fn); +#else + snprintf(s, l, "%s\\%s", homedir, fn); +#endif + return s; + } + + return fn; +} + +/* Load a cookie from a file in the home directory. If the specified + * path starts with /, use it as absolute path instead. */ +int pa_authkey_load_auto(const char *fn, void *data, size_t length) { + char path[PATH_MAX]; + const char *p; + assert(fn && data && length); + + if (!(p = normalize_path(fn, path, sizeof(path)))) + return -2; + + return pa_authkey_load(p, data, length); +} + +/* Store the specified cookie in the speicified cookie file */ +int pa_authkey_save(const char *fn, const void *data, size_t length) { + int fd = -1; + int unlock = 0, ret = -1; + ssize_t r; + char path[PATH_MAX]; + const char *p; + assert(fn && data && length); + + if (!(p = normalize_path(fn, path, sizeof(path)))) + return -2; + + if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { + pa_log(__FILE__": failed to open cookie file '%s': %s", fn, pa_cstrerror(errno)); + goto finish; + } + + unlock = pa_lock_fd(fd, 1) >= 0; + + if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) { + pa_log(__FILE__": failed to read cookie file '%s': %s", fn, pa_cstrerror(errno)); + goto finish; + } + + ret = 0; + +finish: + + if (fd >= 0) { + + if (unlock) + pa_lock_fd(fd, 0); + + close(fd); + } + + return ret; +} diff --git a/src/pulsecore/authkey.h b/src/pulsecore/authkey.h new file mode 100644 index 00000000..cc8a565c --- /dev/null +++ b/src/pulsecore/authkey.h @@ -0,0 +1,32 @@ +#ifndef fooauthkeyhfoo +#define fooauthkeyhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <sys/types.h> + +int pa_authkey_load(const char *path, void *data, size_t len); +int pa_authkey_load_auto(const char *fn, void *data, size_t length); + +int pa_authkey_save(const char *path, const void *data, size_t length); + +#endif diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c new file mode 100644 index 00000000..f6869097 --- /dev/null +++ b/src/pulsecore/autoload.c @@ -0,0 +1,182 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/module.h> +#include <pulsecore/memchunk.h> +#include <pulsecore/sound-file.h> +#include <pulsecore/log.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/core-subscribe.h> + +#include "autoload.h" + +static void entry_free(pa_autoload_entry *e) { + assert(e); + pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX); + pa_xfree(e->name); + pa_xfree(e->module); + pa_xfree(e->argument); + pa_xfree(e); +} + +static void entry_remove_and_free(pa_autoload_entry *e) { + assert(e && e->core); + + pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL); + pa_hashmap_remove(e->core->autoload_hashmap, e->name); + entry_free(e); +} + +static pa_autoload_entry* entry_new(pa_core *c, const char *name) { + pa_autoload_entry *e = NULL; + assert(c && name); + + if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name))) + return NULL; + + e = pa_xmalloc(sizeof(pa_autoload_entry)); + e->core = c; + e->name = pa_xstrdup(name); + e->module = e->argument = NULL; + e->in_action = 0; + + if (!c->autoload_hashmap) + c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(c->autoload_hashmap); + + pa_hashmap_put(c->autoload_hashmap, e->name, e); + + if (!c->autoload_idxset) + c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + pa_idxset_put(c->autoload_idxset, e, &e->index); + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index); + + return e; +} + +int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) { + pa_autoload_entry *e = NULL; + assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE)); + + if (!(e = entry_new(c, name))) + return -1; + + e->module = pa_xstrdup(module); + e->argument = pa_xstrdup(argument); + e->type = type; + + if (idx) + *idx = e->index; + + return 0; +} + +int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) { + pa_autoload_entry *e; + assert(c && name && type); + + if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type) + return -1; + + entry_remove_and_free(e); + return 0; +} + +int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) { + pa_autoload_entry *e; + assert(c && idx != PA_IDXSET_INVALID); + + if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx))) + return -1; + + entry_remove_and_free(e); + return 0; +} + +void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) { + pa_autoload_entry *e; + pa_module *m; + assert(c && name); + + if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type)) + return; + + if (e->in_action) + return; + + e->in_action = 1; + + if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) { + if ((m = pa_module_load(c, e->module, e->argument))) + m->auto_unload = 1; + } + + e->in_action = 0; +} + +static void free_func(void *p, PA_GCC_UNUSED void *userdata) { + pa_autoload_entry *e = p; + pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL); + entry_free(e); +} + +void pa_autoload_free(pa_core *c) { + if (c->autoload_hashmap) { + pa_hashmap_free(c->autoload_hashmap, free_func, NULL); + c->autoload_hashmap = NULL; + } + + if (c->autoload_idxset) { + pa_idxset_free(c->autoload_idxset, NULL, NULL); + c->autoload_idxset = NULL; + } +} + +const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) { + pa_autoload_entry *e; + assert(c && name); + + if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type) + return NULL; + + return e; +} + +const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) { + pa_autoload_entry *e; + assert(c && idx != PA_IDXSET_INVALID); + + if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx))) + return NULL; + + return e; +} diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h new file mode 100644 index 00000000..65bdd6da --- /dev/null +++ b/src/pulsecore/autoload.h @@ -0,0 +1,58 @@ +#ifndef fooautoloadhfoo +#define fooautoloadhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/namereg.h> + +/* Using the autoloading facility, modules by be loaded on-demand and + * synchronously. The user may register a "ghost sink" or "ghost + * source". Whenever this sink/source is requested but not available a + * specified module is loaded. */ + +/* An autoload entry, or "ghost" sink/source */ +typedef struct pa_autoload_entry { + pa_core *core; + uint32_t index; + char *name; + pa_namereg_type_t type; /* Type of the autoload entry */ + int in_action; /* Currently loaded */ + char *module, *argument; +} pa_autoload_entry; + +/* Add a new autoload entry of the given time, with the speicified + * sink/source name, module name and argument. Return the entry's + * index in *index */ +int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx); + +/* Free all autoload entries */ +void pa_autoload_free(pa_core *c); +int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type); +int pa_autoload_remove_by_index(pa_core *c, uint32_t idx); + +/* Request an autoload entry by its name, effectively causing a module to be loaded */ +void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type); + +const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type); +const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx); + +#endif diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c new file mode 100644 index 00000000..4ba3e0af --- /dev/null +++ b/src/pulsecore/cli-command.c @@ -0,0 +1,947 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <errno.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/module.h> +#include <pulsecore/sink.h> +#include <pulsecore/source.h> +#include <pulsecore/client.h> +#include <pulsecore/sink-input.h> +#include <pulsecore/source-output.h> +#include <pulsecore/tokenizer.h> +#include <pulsecore/strbuf.h> +#include <pulsecore/namereg.h> +#include <pulsecore/cli-text.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/sample-util.h> +#include <pulsecore/sound-file.h> +#include <pulsecore/play-memchunk.h> +#include <pulsecore/autoload.h> +#include <pulsecore/sound-file-stream.h> +#include <pulsecore/props.h> +#include <pulsecore/core-util.h> +#include <pulsecore/core-error.h> + +#include "cli-command.h" + +struct command { + const char *name; + int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail); + const char *help; + unsigned args; +}; + +#define INCLUDE_META ".include" +#define FAIL_META ".fail" +#define NOFAIL_META ".nofail" + +/* Prototypes for all available commands */ +static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); + + +/* A method table for all available commands */ + +static const struct command commands[] = { + { "exit", pa_cli_command_exit, "Terminate the daemon", 1 }, + { "help", pa_cli_command_help, "Show this help", 1 }, + { "list-modules", pa_cli_command_modules, "List loaded modules", 1 }, + { "list-sinks", pa_cli_command_sinks, "List loaded sinks", 1 }, + { "list-sources", pa_cli_command_sources, "List loaded sources", 1 }, + { "list-clients", pa_cli_command_clients, "List loaded clients", 1 }, + { "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 }, + { "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 }, + { "stat", pa_cli_command_stat, "Show memory block statistics", 1 }, + { "info", pa_cli_command_info, "Show comprehensive status", 1 }, + { "ls", pa_cli_command_info, NULL, 1 }, + { "list", pa_cli_command_info, NULL, 1 }, + { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3}, + { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2}, + { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3}, + { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3}, + { "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3}, + { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, mute)", 3}, + { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, mute)", 3}, + { "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2}, + { "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2}, + { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2}, + { "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2}, + { "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2}, + { "list-samples", pa_cli_command_scache_list, "List all entries in the sample cache", 1}, + { "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3}, + { "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2}, + { "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3}, + { "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3}, + { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2}, + { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3}, + { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1}, + { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4}, + { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4}, + { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2}, + { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2}, + { "dump", pa_cli_command_dump, "Dump daemon configuration", 1}, + { "list-props", pa_cli_command_list_props, NULL, 1}, + { NULL, NULL, NULL, 0 } +}; + +static const char whitespace[] = " \t\n\r"; +static const char linebreak[] = "\n\r"; + +static uint32_t parse_index(const char *n) { + uint32_t idx; + + if (pa_atou(n, &idx) < 0) + return (uint32_t) PA_IDXSET_INVALID; + + return idx; +} + +static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + assert(c && c->mainloop && t); + c->mainloop->quit(c->mainloop, 0); + return 0; +} + +static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const struct command*command; + assert(c && t && buf); + + pa_strbuf_puts(buf, "Available commands:\n"); + + for (command = commands; command->name; command++) + if (command->help) + pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help); + return 0; +} + +static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_module_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_client_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_sink_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_source_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_sink_input_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_source_output_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char s[256]; + assert(c && t); + + pa_bytes_snprint(s, sizeof(s), c->memblock_stat->total_size); + pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n", + c->memblock_stat->total, + s); + + pa_bytes_snprint(s, sizeof(s), c->memblock_stat->allocated_size); + pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n", + c->memblock_stat->allocated, + s); + + pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c)); + pa_strbuf_printf(buf, "Total sample cache size: %s.\n", s); + + pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec); + pa_strbuf_printf(buf, "Default sample spec: %s\n", s); + + pa_strbuf_printf(buf, "Default sink name: %s\n" + "Default source name: %s\n", + pa_namereg_get_default_sink_name(c), + pa_namereg_get_default_source_name(c)); + + return 0; +} + +static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + assert(c && t); + pa_cli_command_stat(c, t, buf, fail); + pa_cli_command_modules(c, t, buf, fail); + pa_cli_command_sinks(c, t, buf, fail); + pa_cli_command_sources(c, t, buf, fail); + pa_cli_command_clients(c, t, buf, fail); + pa_cli_command_sink_inputs(c, t, buf, fail); + pa_cli_command_source_outputs(c, t, buf, fail); + pa_cli_command_scache_list(c, t, buf, fail); + pa_cli_command_autoload_list(c, t, buf, fail); + return 0; +} + +static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + pa_module *m; + const char *name; + assert(c && t); + + if (!(name = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n"); + return -1; + } + + if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) { + pa_strbuf_puts(buf, "Module load failed.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + pa_module *m; + uint32_t idx; + const char *i; + char *e; + assert(c && t); + + if (!(i = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify the module index.\n"); + return -1; + } + + idx = (uint32_t) strtoul(i, &e, 10); + if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) { + pa_strbuf_puts(buf, "Invalid module index.\n"); + return -1; + } + + pa_module_unload_request(m); + return 0; +} + +static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n, *v; + pa_sink *sink; + uint32_t volume; + pa_cvolume cvolume; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + if (!(v = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + return -1; + } + + if (pa_atou(v, &volume) < 0) { + pa_strbuf_puts(buf, "Failed to parse volume.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { + pa_strbuf_puts(buf, "No sink found by this name or index.\n"); + return -1; + } + + pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); + pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume); + return 0; +} + +static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n, *v; + pa_sink_input *si; + pa_volume_t volume; + pa_cvolume cvolume; + uint32_t idx; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(v = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + return -1; + } + + if (pa_atou(v, &volume) < 0) { + pa_strbuf_puts(buf, "Failed to parse volume.\n"); + return -1; + } + + if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } + + pa_cvolume_set(&cvolume, si->sample_spec.channels, volume); + pa_sink_input_set_volume(si, &cvolume); + return 0; +} + +static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n, *v; + pa_source *source; + uint32_t volume; + pa_cvolume cvolume; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); + return -1; + } + + if (!(v = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + return -1; + } + + if (pa_atou(v, &volume) < 0) { + pa_strbuf_puts(buf, "Failed to parse volume.\n"); + return -1; + } + + if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) { + pa_strbuf_puts(buf, "No source found by this name or index.\n"); + return -1; + } + + pa_cvolume_set(&cvolume, source->sample_spec.channels, volume); + pa_source_set_volume(source, PA_MIXER_HARDWARE, &cvolume); + return 0; +} + +static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n, *m; + pa_sink *sink; + int mute; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + if (!(m = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n"); + return -1; + } + + if (pa_atoi(m, &mute) < 0) { + pa_strbuf_puts(buf, "Failed to parse mute switch.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { + pa_strbuf_puts(buf, "No sink found by this name or index.\n"); + return -1; + } + + pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute); + return 0; +} + +static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n, *m; + pa_source *source; + int mute; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); + return -1; + } + + if (!(m = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n"); + return -1; + } + + if (pa_atoi(m, &mute) < 0) { + pa_strbuf_puts(buf, "Failed to parse mute switch.\n"); + return -1; + } + + if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) { + pa_strbuf_puts(buf, "No sink found by this name or index.\n"); + return -1; + } + + pa_source_set_mute(source, PA_MIXER_HARDWARE, mute); + return 0; +} + +static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + pa_namereg_set_default(c, n, PA_NAMEREG_SINK); + return 0; +} + +static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); + return -1; + } + + pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE); + return 0; +} + +static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + pa_client *client; + uint32_t idx; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a client by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(client = pa_idxset_get_by_index(c->clients, idx))) { + pa_strbuf_puts(buf, "No client found by this index.\n"); + return -1; + } + + pa_client_kill(client); + return 0; +} + +static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + pa_sink_input *sink_input; + uint32_t idx; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) { + pa_strbuf_puts(buf, "No sink input found by this index.\n"); + return -1; + } + + pa_sink_input_kill(sink_input); + return 0; +} + +static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + pa_source_output *source_output; + uint32_t idx; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source output by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) { + pa_strbuf_puts(buf, "No source output found by this index.\n"); + return -1; + } + + pa_source_output_kill(source_output); + return 0; +} + +static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_scache_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *n, *sink_name; + pa_sink *sink; + assert(c && t && buf && fail); + + if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { + pa_strbuf_puts(buf, "No sink by that name.\n"); + return -1; + } + + if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) { + pa_strbuf_puts(buf, "Failed to play sample.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *n; + assert(c && t && buf && fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sample name.\n"); + return -1; + } + + if (pa_scache_remove_item(c, n) < 0) { + pa_strbuf_puts(buf, "Failed to remove sample.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *fname, *n; + int r; + assert(c && t && buf && fail); + + if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n"); + return -1; + } + + if (strstr(pa_tokenizer_get(t, 0), "lazy")) + r = pa_scache_add_file_lazy(c, n, fname, NULL); + else + r = pa_scache_add_file(c, n, fname, NULL); + + if (r < 0) + pa_strbuf_puts(buf, "Failed to load sound file.\n"); + + return 0; +} + +static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *pname; + assert(c && t && buf && fail); + + if (!(pname = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a path name.\n"); + return -1; + } + + if (pa_scache_add_directory_lazy(c, pname) < 0) { + pa_strbuf_puts(buf, "Failed to load directory.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *fname, *sink_name; + pa_sink *sink; + assert(c && t && buf && fail); + + if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { + pa_strbuf_puts(buf, "No sink by that name.\n"); + return -1; + } + + + return pa_play_file(sink, fname, NULL); +} + +static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *a, *b; + assert(c && t && buf && fail); + + if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n"); + return -1; + } + + pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL); + + return 0; +} + +static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *name; + assert(c && t && buf && fail); + + if (!(name = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a device name\n"); + return -1; + } + + if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) { + pa_strbuf_puts(buf, "Failed to remove autload entry\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_autoload_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + assert(c && t); + pa_property_dump(c, buf); + return 0; +} + +static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + pa_module *m; + pa_sink *sink; + pa_source *source; + int nl; + const char *p; + uint32_t idx; + char txt[256]; + time_t now; + void *i; + pa_autoload_entry *a; + + assert(c && t); + + time(&now); + +#ifdef HAVE_CTIME_R + pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt)); +#else + pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now)); +#endif + + + for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) { + if (m->auto_unload) + continue; + + pa_strbuf_printf(buf, "load-module %s", m->name); + + if (m->argument) + pa_strbuf_printf(buf, " %s", m->argument); + + pa_strbuf_puts(buf, "\n"); + } + + nl = 0; + + for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { + if (sink->owner && sink->owner->auto_unload) + continue; + + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + + pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, PA_MIXER_HARDWARE))); + pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink, PA_MIXER_HARDWARE)); + } + + for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { + if (source->owner && source->owner->auto_unload) + continue; + + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + + pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, PA_MIXER_HARDWARE))); + pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source, PA_MIXER_HARDWARE)); + } + + + if (c->autoload_hashmap) { + nl = 0; + + i = NULL; + while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) { + + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + + pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module); + + if (a->argument) + pa_strbuf_printf(buf, " %s", a->argument); + + pa_strbuf_puts(buf, "\n"); + } + } + + nl = 0; + + if ((p = pa_namereg_get_default_sink_name(c))) { + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + pa_strbuf_printf(buf, "set-default-sink %s\n", p); + } + + if ((p = pa_namereg_get_default_source_name(c))) { + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + pa_strbuf_printf(buf, "set-default-source %s\n", p); + } + + pa_strbuf_puts(buf, "\n### EOF\n"); + + return 0; +} + + +int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) { + const char *cs; + + cs = s+strspn(s, whitespace); + + if (*cs == '#' || !*cs) + return 0; + else if (*cs == '.') { + if (!strcmp(cs, FAIL_META)) + *fail = 1; + else if (!strcmp(cs, NOFAIL_META)) + *fail = 0; + else { + size_t l; + l = strcspn(cs, whitespace); + + if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) { + const char *filename = cs+l+strspn(cs+l, whitespace); + + if (pa_cli_command_execute_file(c, filename, buf, fail) < 0) + if (*fail) return -1; + } else { + pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs); + if (*fail) return -1; + } + } + } else { + const struct command*command; + int unknown = 1; + size_t l; + + l = strcspn(cs, whitespace); + + for (command = commands; command->name; command++) + if (strlen(command->name) == l && !strncmp(cs, command->name, l)) { + int ret; + pa_tokenizer *t = pa_tokenizer_new(cs, command->args); + assert(t); + ret = command->proc(c, t, buf, fail); + pa_tokenizer_free(t); + unknown = 0; + + if (ret < 0 && *fail) + return -1; + + break; + } + + if (unknown) { + pa_strbuf_printf(buf, "Unknown command: %s\n", cs); + if (*fail) + return -1; + } + } + + return 0; +} + +int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) { + char line[256]; + FILE *f = NULL; + int ret = -1; + assert(c && fn && buf); + + if (!(f = fopen(fn, "r"))) { + pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno)); + if (!*fail) + ret = 0; + goto fail; + } + + while (fgets(line, sizeof(line), f)) { + char *e = line + strcspn(line, linebreak); + *e = 0; + + if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail) + goto fail; + } + + ret = 0; + +fail: + if (f) + fclose(f); + + return ret; +} + +int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) { + const char *p; + assert(c && s && buf && fail); + + p = s; + while (*p) { + size_t l = strcspn(p, linebreak); + char *line = pa_xstrndup(p, l); + + if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) { + pa_xfree(line); + return -1; + } + pa_xfree(line); + + p += l; + p += strspn(p, linebreak); + } + + return 0; +} diff --git a/src/pulsecore/cli-command.h b/src/pulsecore/cli-command.h new file mode 100644 index 00000000..c56c3ca0 --- /dev/null +++ b/src/pulsecore/cli-command.h @@ -0,0 +1,40 @@ +#ifndef fooclicommandhfoo +#define fooclicommandhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/strbuf.h> +#include <pulsecore/core.h> + +/* Execute a single CLI command. Write the results to the string + * buffer *buf. If *fail is non-zero the function will return -1 when + * one or more of the executed commands failed. *fail + * may be modified by the function call. */ +int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail); + +/* Execute a whole file of CLI commands */ +int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail); + +/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */ +int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail); + +#endif diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c new file mode 100644 index 00000000..eecf68ff --- /dev/null +++ b/src/pulsecore/cli-text.c @@ -0,0 +1,387 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <string.h> + +#include <pulse/volume.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/module.h> +#include <pulsecore/client.h> +#include <pulsecore/sink.h> +#include <pulsecore/source.h> +#include <pulsecore/sink-input.h> +#include <pulsecore/source-output.h> +#include <pulsecore/strbuf.h> +#include <pulsecore/sample-util.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/autoload.h> + +#include "cli-text.h" + +char *pa_module_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_module *m; + uint32_t idx = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules)); + + for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) + pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n\tused: %i\n\tauto unload: %s\n", m->index, m->name, m->argument, m->n_used, m->auto_unload ? "yes" : "no"); + + return pa_strbuf_tostring_free(s); +} + +char *pa_client_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_client *client; + uint32_t idx = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients)); + + for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) { + pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver); + + if (client->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_sink_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_sink *sink; + uint32_t idx = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); + + for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + pa_strbuf_printf( + s, + " %c index: %u\n" + "\tname: <%s>\n" + "\tdriver: <%s>\n" + "\tvolume: <%s>\n" + "\tlatency: <%0.0f usec>\n" + "\tmonitor_source: <%u>\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n", + c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ', + sink->index, sink->name, + sink->driver, + pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)), + (double) pa_sink_get_latency(sink), + sink->monitor_source->index, + pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map)); + + if (sink->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index); + if (sink->description) + pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_source_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_source *source; + uint32_t idx = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); + + for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + + pa_strbuf_printf( + s, + " %c index: %u\n" + "\tname: <%s>\n" + "\tdriver: <%s>\n" + "\tlatency: <%0.0f usec>\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n", + c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ', + source->index, + source->name, + source->driver, + (double) pa_source_get_latency(source), + pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map)); + + if (source->monitor_of) + pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); + if (source->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index); + if (source->description) + pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description); + } + + return pa_strbuf_tostring_free(s); +} + + +char *pa_source_output_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_source_output *o; + uint32_t idx = PA_IDXSET_INVALID; + static const char* const state_table[] = { + "RUNNING", + "CORKED", + "DISCONNECTED" + }; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs)); + + for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + assert(o->source); + + pa_strbuf_printf( + s, + " index: %u\n" + "\tname: '%s'\n" + "\tdriver: <%s>\n" + "\tstate: %s\n" + "\tsource: <%u> '%s'\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n" + "\tresample method: %s\n", + o->index, + o->name, + o->driver, + state_table[o->state], + o->source->index, o->source->name, + pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), + pa_resample_method_to_string(pa_source_output_get_resample_method(o))); + if (o->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index); + if (o->client) + pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_sink_input_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_sink_input *i; + uint32_t idx = PA_IDXSET_INVALID; + static const char* const state_table[] = { + "RUNNING", + "CORKED", + "DISCONNECTED" + }; + + assert(c); + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs)); + + for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + assert(i->sink); + + pa_strbuf_printf( + s, + " index: %u\n" + "\tname: <%s>\n" + "\tdriver: <%s>\n" + "\tstate: %s\n" + "\tsink: <%u> '%s'\n" + "\tvolume: <%s>\n" + "\tlatency: <%0.0f usec>\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n" + "\tresample method: %s\n", + i->index, + i->name, + i->driver, + state_table[i->state], + i->sink->index, i->sink->name, + pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), + (double) pa_sink_input_get_latency(i), + pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), + pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); + + if (i->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index); + if (i->client) + pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_scache_list_to_string(pa_core *c) { + pa_strbuf *s; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0); + + if (c->scache) { + pa_scache_entry *e; + uint32_t idx = PA_IDXSET_INVALID; + + for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { + double l = 0; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a"; + + if (e->memchunk.memblock) { + pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec); + pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map); + l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec); + } + + pa_strbuf_printf( + s, + " name: <%s>\n" + "\tindex: <%u>\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n" + "\tlength: <%lu>\n" + "\tduration: <%0.1fs>\n" + "\tvolume: <%s>\n" + "\tlazy: %s\n" + "\tfilename: %s\n", + e->name, + e->index, + ss, + cm, + (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0), + l, + pa_cvolume_snprint(cv, sizeof(cv), &e->volume), + e->lazy ? "yes" : "no", + e->filename ? e->filename : "n/a"); + } + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_autoload_list_to_string(pa_core *c) { + pa_strbuf *s; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0); + + if (c->autoload_hashmap) { + pa_autoload_entry *e; + void *state = NULL; + + while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) { + pa_strbuf_printf( + s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n", + e->name, + e->type == PA_NAMEREG_SOURCE ? "source" : "sink", + e->index, + e->module, + e->argument); + + } + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_full_status_string(pa_core *c) { + pa_strbuf *s; + int i; + + s = pa_strbuf_new(); + + for (i = 0; i < 8; i++) { + char *t = NULL; + + switch (i) { + case 0: + t = pa_sink_list_to_string(c); + break; + case 1: + t = pa_source_list_to_string(c); + break; + case 2: + t = pa_sink_input_list_to_string(c); + break; + case 3: + t = pa_source_output_list_to_string(c); + break; + case 4: + t = pa_client_list_to_string(c); + break; + case 5: + t = pa_module_list_to_string(c); + break; + case 6: + t = pa_scache_list_to_string(c); + break; + case 7: + t = pa_autoload_list_to_string(c); + break; + } + + pa_strbuf_puts(s, t); + pa_xfree(t); + } + + return pa_strbuf_tostring_free(s); +} diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h new file mode 100644 index 00000000..cd3acdee --- /dev/null +++ b/src/pulsecore/cli-text.h @@ -0,0 +1,42 @@ +#ifndef fooclitexthfoo +#define fooclitexthfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/core.h> + +/* Some functions to generate pretty formatted listings of + * entities. The returned strings have to be freed manually. */ + +char *pa_sink_input_list_to_string(pa_core *c); +char *pa_source_output_list_to_string(pa_core *c); +char *pa_sink_list_to_string(pa_core *core); +char *pa_source_list_to_string(pa_core *c); +char *pa_client_list_to_string(pa_core *c); +char *pa_module_list_to_string(pa_core *c); +char *pa_scache_list_to_string(pa_core *c); +char *pa_autoload_list_to_string(pa_core *c); + +char *pa_full_status_string(pa_core *c); + +#endif + diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c new file mode 100644 index 00000000..fbfa5009 --- /dev/null +++ b/src/pulsecore/cli.c @@ -0,0 +1,149 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/ioline.h> +#include <pulsecore/module.h> +#include <pulsecore/sink.h> +#include <pulsecore/source.h> +#include <pulsecore/client.h> +#include <pulsecore/sink-input.h> +#include <pulsecore/source-output.h> +#include <pulsecore/tokenizer.h> +#include <pulsecore/strbuf.h> +#include <pulsecore/namereg.h> +#include <pulsecore/cli-text.h> +#include <pulsecore/cli-command.h> +#include <pulsecore/log.h> + +#include "cli.h" + +#define PROMPT ">>> " + +struct pa_cli { + pa_core *core; + pa_ioline *line; + + void (*eof_callback)(pa_cli *c, void *userdata); + void *userdata; + + pa_client *client; + + int fail, kill_requested, defer_kill; +}; + +static void line_callback(pa_ioline *line, const char *s, void *userdata); +static void client_kill(pa_client *c); + +pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) { + char cname[256]; + pa_cli *c; + assert(io); + + c = pa_xmalloc(sizeof(pa_cli)); + c->core = core; + c->line = pa_ioline_new(io); + assert(c->line); + + c->userdata = NULL; + c->eof_callback = NULL; + + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + c->client = pa_client_new(core, __FILE__, cname); + assert(c->client); + c->client->kill = client_kill; + c->client->userdata = c; + c->client->owner = m; + + pa_ioline_set_callback(c->line, line_callback, c); + pa_ioline_puts(c->line, "Welcome to pulseaudio! Use \"help\" for usage information.\n"PROMPT); + + c->fail = c->kill_requested = c->defer_kill = 0; + + return c; +} + +void pa_cli_free(pa_cli *c) { + assert(c); + pa_ioline_close(c->line); + pa_ioline_unref(c->line); + pa_client_free(c->client); + pa_xfree(c); +} + +static void client_kill(pa_client *client) { + pa_cli *c; + assert(client && client->userdata); + c = client->userdata; + + pa_log_debug(__FILE__": CLI client killed."); + if (c->defer_kill) + c->kill_requested = 1; + else { + if (c->eof_callback) + c->eof_callback(c, c->userdata); + } +} + +static void line_callback(pa_ioline *line, const char *s, void *userdata) { + pa_strbuf *buf; + pa_cli *c = userdata; + char *p; + assert(line && c); + + if (!s) { + pa_log_debug(__FILE__": CLI got EOF from user."); + if (c->eof_callback) + c->eof_callback(c, c->userdata); + + return; + } + + buf = pa_strbuf_new(); + assert(buf); + c->defer_kill++; + pa_cli_command_execute_line(c->core, s, buf, &c->fail); + c->defer_kill--; + pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf)); + pa_xfree(p); + + if (c->kill_requested) { + if (c->eof_callback) + c->eof_callback(c, c->userdata); + } else + pa_ioline_puts(line, PROMPT); +} + +void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) { + assert(c); + c->eof_callback = cb; + c->userdata = userdata; +} diff --git a/src/pulsecore/cli.h b/src/pulsecore/cli.h new file mode 100644 index 00000000..639fa952 --- /dev/null +++ b/src/pulsecore/cli.h @@ -0,0 +1,38 @@ +#ifndef fooclihfoo +#define fooclihfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/iochannel.h> +#include <pulsecore/core.h> +#include <pulsecore/module.h> + +typedef struct pa_cli pa_cli; + +/* Create a new command line session on the specified io channel owned by the specified module */ +pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m); +void pa_cli_free(pa_cli *cli); + +/* Set a callback function that is called whenever the command line session is terminated */ +void pa_cli_set_eof_callback(pa_cli *cli, void (*cb)(pa_cli*c, void *userdata), void *userdata); + +#endif diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c new file mode 100644 index 00000000..bf2b13df --- /dev/null +++ b/src/pulsecore/client.c @@ -0,0 +1,96 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-subscribe.h> +#include <pulsecore/log.h> + +#include "client.h" + +pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) { + pa_client *c; + int r; + assert(core); + + c = pa_xmalloc(sizeof(pa_client)); + c->name = pa_xstrdup(name); + c->driver = pa_xstrdup(driver); + c->owner = NULL; + c->core = core; + + c->kill = NULL; + c->userdata = NULL; + + r = pa_idxset_put(core->clients, c, &c->index); + assert(c->index != PA_IDXSET_INVALID && r >= 0); + + pa_log_info(__FILE__": created %u \"%s\"", c->index, c->name); + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index); + + pa_core_check_quit(core); + + return c; +} + +void pa_client_free(pa_client *c) { + assert(c && c->core); + + pa_idxset_remove_by_data(c->core->clients, c, NULL); + + pa_core_check_quit(c->core); + + pa_log_info(__FILE__": freed %u \"%s\"", c->index, c->name); + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); + pa_xfree(c->name); + pa_xfree(c->driver); + pa_xfree(c); +} + +void pa_client_kill(pa_client *c) { + assert(c); + if (!c->kill) { + pa_log_warn(__FILE__": kill() operation not implemented for client %u", c->index); + return; + } + + c->kill(c); +} + +void pa_client_set_name(pa_client *c, const char *name) { + assert(c); + + pa_log_info(__FILE__": client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name); + + pa_xfree(c->name); + c->name = pa_xstrdup(name); + + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); +} diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h new file mode 100644 index 00000000..1e72baf7 --- /dev/null +++ b/src/pulsecore/client.h @@ -0,0 +1,57 @@ +#ifndef fooclienthfoo +#define fooclienthfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/core.h> +#include <pulsecore/module.h> + +/* Every connection to the server should have a pa_client + * attached. That way the user may generate a listing of all connected + * clients easily and kill them if he wants.*/ + +typedef struct pa_client pa_client; + +struct pa_client { + uint32_t index; + + pa_module *owner; + char *name, *driver; + pa_core *core; + + void (*kill)(pa_client *c); + void *userdata; +}; + +pa_client *pa_client_new(pa_core *c, const char *driver, const char *name); + +/* This function should be called only by the code that created the client */ +void pa_client_free(pa_client *c); + +/* Code that didn't create the client should call this function to + * request destruction of the client */ +void pa_client_kill(pa_client *c); + +/* Rename the client */ +void pa_client_set_name(pa_client *c, const char *name); + +#endif diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c new file mode 100644 index 00000000..cc471ff9 --- /dev/null +++ b/src/pulsecore/conf-parser.c @@ -0,0 +1,181 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/log.h> +#include <pulsecore/core-util.h> + +#include "conf-parser.h" + +#define WHITESPACE " \t\n" +#define COMMENTS "#;\n" + +/* Run the user supplied parser for an assignment */ +static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { + assert(filename && t && lvalue && rvalue); + + for (; t->parse; t++) + if (!strcmp(lvalue, t->lvalue)) + return t->parse(filename, line, lvalue, rvalue, t->data, userdata); + + pa_log(__FILE__": [%s:%u] Unknown lvalue '%s'.", filename, line, lvalue); + + return -1; +} + +/* Returns non-zero when c is contained in s */ +static int in_string(char c, const char *s) { + assert(s); + + for (; *s; s++) + if (*s == c) + return 1; + + return 0; +} + +/* Remove all whitepsapce from the beginning and the end of *s. *s may + * be modified. */ +static char *strip(char *s) { + char *b = s+strspn(s, WHITESPACE); + char *e, *l = NULL; + + for (e = b; *e; e++) + if (!in_string(*e, WHITESPACE)) + l = e; + + if (l) + *(l+1) = 0; + + return b; +} + +/* Parse a variable assignment line */ +static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) { + char *e, *c, *b = l+strspn(l, WHITESPACE); + + if ((c = strpbrk(b, COMMENTS))) + *c = 0; + + if (!*b) + return 0; + + if (!(e = strchr(b, '='))) { + pa_log(__FILE__": [%s:%u] Missing '='.", filename, line); + return -1; + } + + *e = 0; + e++; + + return next_assignment(filename, line, t, strip(b), strip(e), userdata); +} + +/* Go through the file and parse each line */ +int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata) { + int r = -1; + unsigned line = 0; + int do_close = !f; + assert(filename && t); + + if (!f && !(f = fopen(filename, "r"))) { + if (errno == ENOENT) { + r = 0; + goto finish; + } + + pa_log_warn(__FILE__": WARNING: failed to open configuration file '%s': %s", + filename, pa_cstrerror(errno)); + goto finish; + } + + while (!feof(f)) { + char l[256]; + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + pa_log_warn(__FILE__": WARNING: failed to read configuration file '%s': %s", + filename, pa_cstrerror(errno)); + goto finish; + } + + if (parse_line(filename, ++line, t, l, userdata) < 0) + goto finish; + } + + r = 0; + +finish: + + if (do_close && f) + fclose(f); + + return r; +} + +int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + int *i = data; + int32_t k; + assert(filename && lvalue && rvalue && data); + + if (pa_atoi(rvalue, &k) < 0) { + pa_log(__FILE__": [%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); + return -1; + } + + *i = (int) k; + return 0; +} + +int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + int *b = data, k; + assert(filename && lvalue && rvalue && data); + + if ((k = pa_parse_boolean(rvalue)) < 0) { + pa_log(__FILE__": [%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); + return -1; + } + + *b = k; + + return 0; +} + +int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + char **s = data; + assert(filename && lvalue && rvalue && data); + + pa_xfree(*s); + *s = *rvalue ? pa_xstrdup(rvalue) : NULL; + return 0; +} diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h new file mode 100644 index 00000000..9c1a697a --- /dev/null +++ b/src/pulsecore/conf-parser.h @@ -0,0 +1,47 @@ +#ifndef fooconfparserhfoo +#define fooconfparserhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <stdio.h> + +/* An abstract parser for simple, line based, shallow configuration + * files consisting of variable assignments only. */ + +/* Wraps info for parsing a specific configuration variable */ +typedef struct pa_config_item { + const char *lvalue; /* name of the variable */ + int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */ + void *data; /* Where to store the variable's data */ +} pa_config_item; + +/* The configuration file parsing routine. Expects a table of + * pa_config_items in *t that is terminated by an item where lvalue is + * NULL */ +int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata); + +/* Generic parsers for integers, booleans and strings */ +int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); + +#endif diff --git a/src/pulsecore/core-def.h b/src/pulsecore/core-def.h new file mode 100644 index 00000000..718499d1 --- /dev/null +++ b/src/pulsecore/core-def.h @@ -0,0 +1,30 @@ +#ifndef foocoredefhfoo +#define foocoredefhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef enum pa_mixer { + PA_MIXER_SOFTWARE, + PA_MIXER_HARDWARE +} pa_mixer_t; + +#endif diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c new file mode 100644 index 00000000..e42070d1 --- /dev/null +++ b/src/pulsecore/core-error.c @@ -0,0 +1,205 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_PTHREAD +#include <pthread.h> +#endif + +#ifdef HAVE_WINDOWS_H +#include <windows.h> +#endif + +#include <pulse/utf8.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/core-util.h> +#include <pulsecore/native-common.h> + +#include "core-error.h" + +#ifdef HAVE_PTHREAD + +static pthread_once_t cstrerror_once = PTHREAD_ONCE_INIT; +static pthread_key_t tlsstr_key; + +static void inittls(void) { + int ret; + + ret = pthread_key_create(&tlsstr_key, pa_xfree); + if (ret) { + fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", errno); + exit(-1); + } +} + +#elif HAVE_WINDOWS_H + +static DWORD tlsstr_key = TLS_OUT_OF_INDEXES; +static DWORD monitor_key = TLS_OUT_OF_INDEXES; + +static void inittls(void) { + HANDLE mutex; + char name[64]; + + sprintf(name, "pulseaudio%d", (int)GetCurrentProcessId()); + + mutex = CreateMutex(NULL, FALSE, name); + if (!mutex) { + fprintf(stderr, __FILE__ ": CRITICAL: Unable to create named mutex (%d)\n", (int)GetLastError()); + exit(-1); + } + + WaitForSingleObject(mutex, INFINITE); + + if (tlsstr_key == TLS_OUT_OF_INDEXES) { + tlsstr_key = TlsAlloc(); + monitor_key = TlsAlloc(); + if ((tlsstr_key == TLS_OUT_OF_INDEXES) || (monitor_key == TLS_OUT_OF_INDEXES)) { + fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", (int)GetLastError()); + exit(-1); + } + } + + ReleaseMutex(mutex); + + CloseHandle(mutex); +} + +/* + * This is incredibly brain dead, but this is necessary when dealing with + * the hell that is Win32. + */ +struct monitor_data { + HANDLE thread; + void *data; +}; + +static DWORD WINAPI monitor_thread(LPVOID param) { + struct monitor_data *data; + + data = (struct monitor_data*)param; + assert(data); + + WaitForSingleObject(data->thread, INFINITE); + + CloseHandle(data->thread); + pa_xfree(data->data); + pa_xfree(data); + + return 0; +} + +static void start_monitor(void) { + HANDLE thread; + struct monitor_data *data; + + data = pa_xnew(struct monitor_data, 1); + assert(data); + + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &data->thread, 0, FALSE, DUPLICATE_SAME_ACCESS); + + thread = CreateThread(NULL, 0, monitor_thread, data, 0, NULL); + assert(thread); + + TlsSetValue(monitor_key, data); + + CloseHandle(thread); +} + +#else + +/* Unsafe, but we have no choice */ +static char *tlsstr; + +#endif + +const char* pa_cstrerror(int errnum) { + const char *origbuf; + +#ifdef HAVE_STRERROR_R + char errbuf[128]; +#endif + +#ifdef HAVE_PTHREAD + char *tlsstr; + + pthread_once(&cstrerror_once, inittls); + + tlsstr = pthread_getspecific(tlsstr_key); +#elif defined(HAVE_WINDOWS_H) + char *tlsstr; + struct monitor_data *data; + + inittls(); + + tlsstr = TlsGetValue(tlsstr_key); + if (!tlsstr) + start_monitor(); + data = TlsGetValue(monitor_key); +#endif + + if (tlsstr) + pa_xfree(tlsstr); + +#ifdef HAVE_STRERROR_R + +#ifdef __GLIBC__ + origbuf = strerror_r(errnum, errbuf, sizeof(errbuf)); + if (origbuf == NULL) + origbuf = ""; +#else + if (strerror_r(errnum, errbuf, sizeof(errbuf)) == 0) { + origbuf = errbuf; + errbuf[sizeof(errbuf) - 1] = '\0'; + } else + origbuf = ""; +#endif + +#else + /* This might not be thread safe, but we hope for the best */ + origbuf = strerror(errnum); +#endif + + tlsstr = pa_locale_to_utf8(origbuf); + if (!tlsstr) { + fprintf(stderr, "Unable to convert, filtering\n"); + tlsstr = pa_utf8_filter(origbuf); + } + +#ifdef HAVE_PTHREAD + pthread_setspecific(tlsstr_key, tlsstr); +#elif defined(HAVE_WINDOWS_H) + TlsSetValue(tlsstr_key, tlsstr); + data->data = tlsstr; +#endif + + return tlsstr; +} diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h new file mode 100644 index 00000000..32da8bf2 --- /dev/null +++ b/src/pulsecore/core-error.h @@ -0,0 +1,41 @@ +#ifndef foocoreerrorhfoo +#define foocoreerrorhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> +#include <pulse/cdecl.h> + +/** \file + * Error management */ + +PA_C_DECL_BEGIN + +/** A wrapper around the standard strerror() function that converts the + * string to UTF-8. The function is thread safe but the returned string is + * only guaranteed to exist until the thread exits or pa_cstrerror() is + * called again from the same thread. */ +const char* pa_cstrerror(int errnum); + +PA_C_DECL_END + +#endif diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c new file mode 100644 index 00000000..377dd569 --- /dev/null +++ b/src/pulsecore/core-scache.c @@ -0,0 +1,412 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <errno.h> +#include <limits.h> + +#ifdef HAVE_GLOB_H +#include <glob.h> +#endif + +#ifdef HAVE_WINDOWS_H +#include <windows.h> +#endif + +#include <pulse/mainloop.h> +#include <pulse/channelmap.h> +#include <pulse/timeval.h> +#include <pulse/util.h> +#include <pulse/volume.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/sink-input.h> +#include <pulsecore/sample-util.h> +#include <pulsecore/play-memchunk.h> +#include <pulsecore/core-subscribe.h> +#include <pulsecore/namereg.h> +#include <pulsecore/sound-file.h> +#include <pulsecore/core-util.h> +#include <pulsecore/log.h> +#include <pulsecore/core-error.h> + +#include "core-scache.h" + +#define UNLOAD_POLL_TIME 2 + +static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + pa_core *c = userdata; + struct timeval ntv; + assert(c && c->mainloop == m && c->scache_auto_unload_event == e); + + pa_scache_unload_unused(c); + + pa_gettimeofday(&ntv); + ntv.tv_sec += UNLOAD_POLL_TIME; + m->time_restart(e, &ntv); +} + +static void free_entry(pa_scache_entry *e) { + assert(e); + pa_namereg_unregister(e->core, e->name); + pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index); + pa_xfree(e->name); + pa_xfree(e->filename); + if (e->memchunk.memblock) + pa_memblock_unref(e->memchunk.memblock); + pa_xfree(e); +} + +static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { + pa_scache_entry *e; + assert(c && name); + + if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) { + if (e->memchunk.memblock) + pa_memblock_unref(e->memchunk.memblock); + + pa_xfree(e->filename); + + assert(e->core == c); + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); + } else { + e = pa_xmalloc(sizeof(pa_scache_entry)); + + if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) { + pa_xfree(e); + return NULL; + } + + e->name = pa_xstrdup(name); + e->core = c; + + if (!c->scache) { + c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + assert(c->scache); + } + + pa_idxset_put(c->scache, e, &e->index); + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index); + } + + e->last_used_time = 0; + e->memchunk.memblock = NULL; + e->memchunk.index = e->memchunk.length = 0; + e->filename = NULL; + e->lazy = 0; + e->last_used_time = 0; + + memset(&e->sample_spec, 0, sizeof(e->sample_spec)); + pa_channel_map_init(&e->channel_map); + pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX); + + return e; +} + +int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) { + pa_scache_entry *e; + assert(c && name); + + if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX) + return -1; + + if (!(e = scache_add_item(c, name))) + return -1; + + if (ss) { + e->sample_spec = *ss; + pa_channel_map_init_auto(&e->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT); + e->volume.channels = e->sample_spec.channels; + } + + if (map) + e->channel_map = *map; + + if (chunk) { + e->memchunk = *chunk; + pa_memblock_ref(e->memchunk.memblock); + } + + if (idx) + *idx = e->index; + + return 0; +} + +int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) { + pa_sample_spec ss; + pa_channel_map map; + pa_memchunk chunk; + int r; + +#ifdef OS_IS_WIN32 + char buf[MAX_PATH]; + + if (ExpandEnvironmentStrings(filename, buf, MAX_PATH)) + filename = buf; +#endif + + if (pa_sound_file_load(filename, &ss, &map, &chunk, c->memblock_stat) < 0) + return -1; + + r = pa_scache_add_item(c, name, &ss, &map, &chunk, idx); + pa_memblock_unref(chunk.memblock); + + return r; +} + +int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) { + pa_scache_entry *e; + +#ifdef OS_IS_WIN32 + char buf[MAX_PATH]; + + if (ExpandEnvironmentStrings(filename, buf, MAX_PATH)) + filename = buf; +#endif + + assert(c && name); + + if (!(e = scache_add_item(c, name))) + return -1; + + e->lazy = 1; + e->filename = pa_xstrdup(filename); + + if (!c->scache_auto_unload_event) { + struct timeval ntv; + pa_gettimeofday(&ntv); + ntv.tv_sec += UNLOAD_POLL_TIME; + c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c); + } + + if (idx) + *idx = e->index; + + return 0; +} + +int pa_scache_remove_item(pa_core *c, const char *name) { + pa_scache_entry *e; + assert(c && name); + + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) + return -1; + + if (pa_idxset_remove_by_data(c->scache, e, NULL) != e) + assert(0); + + free_entry(e); + return 0; +} + +static void free_cb(void *p, PA_GCC_UNUSED void *userdata) { + pa_scache_entry *e = p; + assert(e); + free_entry(e); +} + +void pa_scache_free(pa_core *c) { + assert(c); + + if (c->scache) { + pa_idxset_free(c->scache, free_cb, NULL); + c->scache = NULL; + } + + if (c->scache_auto_unload_event) + c->mainloop->time_free(c->scache_auto_unload_event); +} + +int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume) { + pa_scache_entry *e; + char *t; + pa_cvolume r; + + assert(c); + assert(name); + assert(sink); + + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1))) + return -1; + + if (e->lazy && !e->memchunk.memblock) { + if (pa_sound_file_load(e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, c->memblock_stat) < 0) + return -1; + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); + + if (e->volume.channels > e->sample_spec.channels) + e->volume.channels = e->sample_spec.channels; + } + + if (!e->memchunk.memblock) + return -1; + + t = pa_sprintf_malloc("sample:%s", name); + + pa_cvolume_set(&r, e->volume.channels, volume); + pa_sw_cvolume_multiply(&r, &r, &e->volume); + + if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, &r) < 0) { + pa_xfree(t); + return -1; + } + + pa_xfree(t); + + if (e->lazy) + time(&e->last_used_time); + + return 0; +} + +const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) { + pa_scache_entry *e; + assert(c && id != PA_IDXSET_INVALID); + + if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id))) + return NULL; + + return e->name; +} + +uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) { + pa_scache_entry *e; + assert(c && name); + + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) + return PA_IDXSET_INVALID; + + return e->index; +} + +uint32_t pa_scache_total_size(pa_core *c) { + pa_scache_entry *e; + uint32_t idx, sum = 0; + assert(c); + + if (!c->scache || !pa_idxset_size(c->scache)) + return 0; + + for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) + if (e->memchunk.memblock) + sum += e->memchunk.length; + + return sum; +} + +void pa_scache_unload_unused(pa_core *c) { + pa_scache_entry *e; + time_t now; + uint32_t idx; + assert(c); + + if (!c->scache || !pa_idxset_size(c->scache)) + return; + + time(&now); + + for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { + + if (!e->lazy || !e->memchunk.memblock) + continue; + + if (e->last_used_time + c->scache_idle_time > now) + continue; + + pa_memblock_unref(e->memchunk.memblock); + e->memchunk.memblock = NULL; + e->memchunk.index = e->memchunk.length = 0; + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); + } +} + +static void add_file(pa_core *c, const char *pathname) { + struct stat st; + const char *e; + + e = pa_path_get_filename(pathname); + + if (stat(pathname, &st) < 0) { + pa_log(__FILE__": stat('%s'): %s", pathname, pa_cstrerror(errno)); + return; + } + +#if defined(S_ISREG) && defined(S_ISLNK) + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) +#endif + pa_scache_add_file_lazy(c, e, pathname, NULL); +} + +int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) { + DIR *dir; + assert(c && pathname); + + /* First try to open this as directory */ + if (!(dir = opendir(pathname))) { +#ifdef HAVE_GLOB_H + glob_t p; + unsigned int i; + /* If that fails, try to open it as shell glob */ + + if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) { + pa_log(__FILE__": failed to open directory '%s': %s", pathname, pa_cstrerror(errno)); + return -1; + } + + for (i = 0; i < p.gl_pathc; i++) + add_file(c, p.gl_pathv[i]); + + globfree(&p); +#else + return -1; +#endif + } else { + struct dirent *e; + + while ((e = readdir(dir))) { + char p[PATH_MAX]; + + if (e->d_name[0] == '.') + continue; + + snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name); + add_file(c, p); + } + } + + closedir(dir); + return 0; +} diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h new file mode 100644 index 00000000..d01aae9b --- /dev/null +++ b/src/pulsecore/core-scache.h @@ -0,0 +1,64 @@ +#ifndef foocorescachehfoo +#define foocorescachehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/core.h> +#include <pulsecore/memchunk.h> +#include <pulsecore/sink.h> + +#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*2) + +typedef struct pa_scache_entry { + pa_core *core; + uint32_t index; + char *name; + + pa_cvolume volume; + pa_sample_spec sample_spec; + pa_channel_map channel_map; + pa_memchunk memchunk; + + char *filename; + + int lazy; + time_t last_used_time; +} pa_scache_entry; + +int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx); +int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx); +int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx); + +int pa_scache_add_directory_lazy(pa_core *c, const char *pathname); + +int pa_scache_remove_item(pa_core *c, const char *name); +int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume); +void pa_scache_free(pa_core *c); + +const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id); +uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name); + +uint32_t pa_scache_total_size(pa_core *c); + +void pa_scache_unload_unused(pa_core *c); + +#endif diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c new file mode 100644 index 00000000..e865256a --- /dev/null +++ b/src/pulsecore/core-subscribe.c @@ -0,0 +1,233 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <assert.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/queue.h> +#include <pulsecore/log.h> + +#include "core-subscribe.h" + +/* The subscription subsystem may be used to be notified whenever an + * entity (sink, source, ...) is created or deleted. Modules may + * register a callback function that is called whenever an event + * matching a subscription mask happens. The execution of the callback + * function is postponed to the next main loop iteration, i.e. is not + * called from within the stack frame the entity was created in. */ + +struct pa_subscription { + pa_core *core; + int dead; + void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata); + void *userdata; + pa_subscription_mask_t mask; + + pa_subscription *prev, *next; +}; + +struct pa_subscription_event { + pa_subscription_event_type_t type; + uint32_t index; +}; + +static void sched_event(pa_core *c); + +/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */ +pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) { + pa_subscription *s; + assert(c); + + s = pa_xmalloc(sizeof(pa_subscription)); + s->core = c; + s->dead = 0; + s->callback = callback; + s->userdata = userdata; + s->mask = m; + + if ((s->next = c->subscriptions)) + s->next->prev = s; + s->prev = NULL; + c->subscriptions = s; + return s; +} + +/* Free a subscription object, effectively marking it for deletion */ +void pa_subscription_free(pa_subscription*s) { + assert(s && !s->dead); + s->dead = 1; + sched_event(s->core); +} + +static void free_item(pa_subscription *s) { + assert(s && s->core); + + if (s->prev) + s->prev->next = s->next; + else + s->core->subscriptions = s->next; + + if (s->next) + s->next->prev = s->prev; + + pa_xfree(s); +} + +/* Free all subscription objects */ +void pa_subscription_free_all(pa_core *c) { + pa_subscription_event *e; + assert(c); + + while (c->subscriptions) + free_item(c->subscriptions); + + if (c->subscription_event_queue) { + while ((e = pa_queue_pop(c->subscription_event_queue))) + pa_xfree(e); + + pa_queue_free(c->subscription_event_queue, NULL, NULL); + c->subscription_event_queue = NULL; + } + + if (c->subscription_defer_event) { + c->mainloop->defer_free(c->subscription_defer_event); + c->subscription_defer_event = NULL; + } +} + +#if 0 +static void dump_event(pa_subscription_event*e) { + switch (e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SINK: + pa_log(__FILE__": SINK_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_SOURCE: + pa_log(__FILE__": SOURCE_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_SINK_INPUT: + pa_log(__FILE__": SINK_INPUT_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: + pa_log(__FILE__": SOURCE_OUTPUT_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_MODULE: + pa_log(__FILE__": MODULE_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_CLIENT: + pa_log(__FILE__": CLIENT_EVENT"); + break; + default: + pa_log(__FILE__": OTHER"); + break; + } + + switch (e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) { + case PA_SUBSCRIPTION_EVENT_NEW: + pa_log(__FILE__": NEW"); + break; + case PA_SUBSCRIPTION_EVENT_CHANGE: + pa_log(__FILE__": CHANGE"); + break; + case PA_SUBSCRIPTION_EVENT_REMOVE: + pa_log(__FILE__": REMOVE"); + break; + default: + pa_log(__FILE__": OTHER"); + break; + } + + pa_log(__FILE__": %u", e->index); +} +#endif + +/* Deferred callback for dispatching subscirption events */ +static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) { + pa_core *c = userdata; + pa_subscription *s; + assert(c && c->subscription_defer_event == de && c->mainloop == m); + + c->mainloop->defer_enable(c->subscription_defer_event, 0); + + /* Dispatch queued events */ + + if (c->subscription_event_queue) { + pa_subscription_event *e; + + while ((e = pa_queue_pop(c->subscription_event_queue))) { + + for (s = c->subscriptions; s; s = s->next) { + + if (!s->dead && pa_subscription_match_flags(s->mask, e->type)) + s->callback(c, e->type, e->index, s->userdata); + } + + pa_xfree(e); + } + } + + /* Remove dead subscriptions */ + + s = c->subscriptions; + while (s) { + pa_subscription *n = s->next; + if (s->dead) + free_item(s); + s = n; + } +} + +/* Schedule an mainloop event so that a pending subscription event is dispatched */ +static void sched_event(pa_core *c) { + assert(c); + + if (!c->subscription_defer_event) { + c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c); + assert(c->subscription_defer_event); + } + + c->mainloop->defer_enable(c->subscription_defer_event, 1); +} + +/* Append a new subscription event to the subscription event queue and schedule a main loop event */ +void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t index) { + pa_subscription_event *e; + assert(c); + + e = pa_xmalloc(sizeof(pa_subscription_event)); + e->type = t; + e->index = index; + + if (!c->subscription_event_queue) { + c->subscription_event_queue = pa_queue_new(); + assert(c->subscription_event_queue); + } + + pa_queue_push(c->subscription_event_queue, e); + sched_event(c); +} + + diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h new file mode 100644 index 00000000..c2467033 --- /dev/null +++ b/src/pulsecore/core-subscribe.h @@ -0,0 +1,37 @@ +#ifndef foocoresubscribehfoo +#define foocoresubscribehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_subscription pa_subscription; +typedef struct pa_subscription_event pa_subscription_event; + +#include <pulsecore/core.h> +#include <pulsecore/native-common.h> + +pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata); +void pa_subscription_free(pa_subscription*s); +void pa_subscription_free_all(pa_core *c); + +void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx); + +#endif diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c new file mode 100644 index 00000000..8e3587eb --- /dev/null +++ b/src/pulsecore/core-util.c @@ -0,0 +1,1023 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdarg.h> +#include <stdlib.h> +#include <signal.h> +#include <errno.h> +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <time.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> + +#ifdef HAVE_SCHED_H +#include <sched.h> +#endif + +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + +#ifdef HAVE_PTHREAD +#include <pthread.h> +#endif + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#ifdef HAVE_WINDOWS_H +#include <windows.h> +#endif + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#ifdef HAVE_GRP_H +#include <grp.h> +#endif + +#include <samplerate.h> + +#include <pulse/xmalloc.h> +#include <pulse/util.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/winsock.h> +#include <pulsecore/log.h> + +#include "core-util.h" + +#ifndef OS_IS_WIN32 +#define PA_RUNTIME_PATH_PREFIX "/tmp/pulseaudio-" +#define PATH_SEP '/' +#else +#define PA_RUNTIME_PATH_PREFIX "%TEMP%\\pulseaudio-" +#define PATH_SEP '\\' +#endif + +#ifdef OS_IS_WIN32 + +#define POLYP_ROOTENV "POLYP_ROOT" + +int pa_set_root(HANDLE handle) { + char library_path[MAX_PATH + sizeof(POLYP_ROOTENV) + 1], *sep; + + strcpy(library_path, POLYP_ROOTENV "="); + + if (!GetModuleFileName(handle, library_path + sizeof(POLYP_ROOTENV), MAX_PATH)) + return 0; + + sep = strrchr(library_path, '\\'); + if (sep) + *sep = '\0'; + + if (_putenv(library_path) < 0) + return 0; + + return 1; +} + +#endif + +/** Make a file descriptor nonblock. Doesn't do any error checking */ +void pa_make_nonblock_fd(int fd) { +#ifdef O_NONBLOCK + int v; + assert(fd >= 0); + + if ((v = fcntl(fd, F_GETFL)) >= 0) + if (!(v & O_NONBLOCK)) + fcntl(fd, F_SETFL, v|O_NONBLOCK); +#elif defined(OS_IS_WIN32) + u_long arg = 1; + if (ioctlsocket(fd, FIONBIO, &arg) < 0) { + if (WSAGetLastError() == WSAENOTSOCK) + pa_log_warn(__FILE__": WARNING: Only sockets can be made non-blocking!"); + } +#else + pa_log_warn(__FILE__": WARNING: Non-blocking I/O not supported.!"); +#endif +} + +/** Creates a directory securely */ +int pa_make_secure_dir(const char* dir) { + struct stat st; + assert(dir); + +#ifdef OS_IS_WIN32 + if (mkdir(dir) < 0) +#else + if (mkdir(dir, 0700) < 0) +#endif + if (errno != EEXIST) + return -1; + +#ifdef HAVE_CHOWN + chown(dir, getuid(), getgid()); +#endif +#ifdef HAVE_CHMOD + chmod(dir, 0700); +#endif + +#ifdef HAVE_LSTAT + if (lstat(dir, &st) < 0) +#else + if (stat(dir, &st) < 0) +#endif + goto fail; + +#ifndef OS_IS_WIN32 + if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700)) + goto fail; +#else + fprintf(stderr, "FIXME: pa_make_secure_dir()\n"); +#endif + + return 0; + +fail: + rmdir(dir); + return -1; +} + +/* Return a newly allocated sting containing the parent directory of the specified file */ +char *pa_parent_dir(const char *fn) { + char *slash, *dir = pa_xstrdup(fn); + + slash = (char*) pa_path_get_filename(dir); + if (slash == fn) + return NULL; + + *(slash-1) = 0; + return dir; +} + +/* Creates a the parent directory of the specified path securely */ +int pa_make_secure_parent_dir(const char *fn) { + int ret = -1; + char *dir; + + if (!(dir = pa_parent_dir(fn))) + goto finish; + + if (pa_make_secure_dir(dir) < 0) + goto finish; + + ret = 0; + +finish: + pa_xfree(dir); + return ret; +} + +/** Platform independent read function. Necessary since not all systems + * treat all file descriptors equal. */ +ssize_t pa_read(int fd, void *buf, size_t count) { + ssize_t r; + +#ifdef OS_IS_WIN32 + r = recv(fd, buf, count, 0); + if (r < 0) { + if (WSAGetLastError() != WSAENOTSOCK) { + errno = WSAGetLastError(); + return r; + } + } + + if (r < 0) +#endif + r = read(fd, buf, count); + + return r; +} + +/** Similar to pa_read(), but handles writes */ +ssize_t pa_write(int fd, const void *buf, size_t count) { + ssize_t r; + +#ifdef OS_IS_WIN32 + r = send(fd, buf, count, 0); + if (r < 0) { + if (WSAGetLastError() != WSAENOTSOCK) { + errno = WSAGetLastError(); + return r; + } + } + + if (r < 0) +#endif + r = write(fd, buf, count); + + return r; +} + +/** Calls read() in a loop. Makes sure that as much as 'size' bytes, + * unless EOF is reached or an error occured */ +ssize_t pa_loop_read(int fd, void*data, size_t size) { + ssize_t ret = 0; + assert(fd >= 0 && data && size); + + while (size > 0) { + ssize_t r; + + if ((r = pa_read(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data = (uint8_t*) data + r; + size -= r; + } + + return ret; +} + +/** Similar to pa_loop_read(), but wraps write() */ +ssize_t pa_loop_write(int fd, const void*data, size_t size) { + ssize_t ret = 0; + assert(fd >= 0 && data && size); + + while (size > 0) { + ssize_t r; + + if ((r = pa_write(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data = (const uint8_t*) data + r; + size -= r; + } + + return ret; +} + +/* Print a warning messages in case that the given signal is not + * blocked or trapped */ +void pa_check_signal_is_blocked(int sig) { +#ifdef HAVE_SIGACTION + struct sigaction sa; + sigset_t set; + + /* If POSIX threads are supported use thread-aware + * pthread_sigmask() function, to check if the signal is + * blocked. Otherwise fall back to sigprocmask() */ + +#ifdef HAVE_PTHREAD + if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) { +#endif + if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) { + pa_log(__FILE__": sigprocmask(): %s", pa_cstrerror(errno)); + return; + } +#ifdef HAVE_PTHREAD + } +#endif + + if (sigismember(&set, sig)) + return; + + /* Check whether the signal is trapped */ + + if (sigaction(sig, NULL, &sa) < 0) { + pa_log(__FILE__": sigaction(): %s", pa_cstrerror(errno)); + return; + } + + if (sa.sa_handler != SIG_DFL) + return; + + pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!", pa_strsignal(sig)); +#else /* HAVE_SIGACTION */ + pa_log(__FILE__": WARNING: %s might not be trapped. This might cause malfunction!", pa_strsignal(sig)); +#endif +} + +/* The following function is based on an example from the GNU libc + * documentation. This function is similar to GNU's asprintf(). */ +char *pa_sprintf_malloc(const char *format, ...) { + int size = 100; + char *c = NULL; + + assert(format); + + for(;;) { + int r; + va_list ap; + + c = pa_xrealloc(c, size); + + va_start(ap, format); + r = vsnprintf(c, size, format, ap); + va_end(ap); + + if (r > -1 && r < size) + return c; + + if (r > -1) /* glibc 2.1 */ + size = r+1; + else /* glibc 2.0 */ + size *= 2; + } +} + +/* Same as the previous function, but use a va_list instead of an + * ellipsis */ +char *pa_vsprintf_malloc(const char *format, va_list ap) { + int size = 100; + char *c = NULL; + + assert(format); + + for(;;) { + int r; + va_list aq; + + va_copy(aq, ap); + + c = pa_xrealloc(c, size); + r = vsnprintf(c, size, format, aq); + + va_end(aq); + + if (r > -1 && r < size) + return c; + + if (r > -1) /* glibc 2.1 */ + size = r+1; + else /* glibc 2.0 */ + size *= 2; + } +} + +/* Similar to OpenBSD's strlcpy() function */ +char *pa_strlcpy(char *b, const char *s, size_t l) { + assert(b && s && l > 0); + + strncpy(b, s, l); + b[l-1] = 0; + return b; +} + +#define NICE_LEVEL (-15) + +/* Raise the priority of the current process as much as possible and +sensible: set the nice level to -15 and enable realtime scheduling if +supported.*/ +void pa_raise_priority(void) { + +#ifdef HAVE_SYS_RESOURCE_H + if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0) + pa_log_warn(__FILE__": setpriority(): %s", pa_cstrerror(errno)); + else + pa_log_info(__FILE__": Successfully gained nice level %i.", NICE_LEVEL); +#endif + +#ifdef _POSIX_PRIORITY_SCHEDULING + { + struct sched_param sp; + + if (sched_getparam(0, &sp) < 0) { + pa_log(__FILE__": sched_getparam(): %s", pa_cstrerror(errno)); + return; + } + + sp.sched_priority = 1; + if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) { + pa_log_warn(__FILE__": sched_setscheduler(): %s", pa_cstrerror(errno)); + return; + } + + pa_log_info(__FILE__": Successfully enabled SCHED_FIFO scheduling."); + } +#endif + +#ifdef OS_IS_WIN32 + if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) + pa_log_warn(__FILE__": SetPriorityClass() failed: 0x%08X", GetLastError()); + else + pa_log_info(__FILE__": Successfully gained high priority class."); +#endif +} + +/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */ +void pa_reset_priority(void) { +#ifdef OS_IS_WIN32 + SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); +#endif + +#ifdef _POSIX_PRIORITY_SCHEDULING + { + struct sched_param sp; + sched_getparam(0, &sp); + sp.sched_priority = 0; + sched_setscheduler(0, SCHED_OTHER, &sp); + } +#endif + +#ifdef HAVE_SYS_RESOURCE_H + setpriority(PRIO_PROCESS, 0, 0); +#endif +} + +/* Set the FD_CLOEXEC flag for a fd */ +int pa_fd_set_cloexec(int fd, int b) { + +#ifdef FD_CLOEXEC + int v; + assert(fd >= 0); + + if ((v = fcntl(fd, F_GETFD, 0)) < 0) + return -1; + + v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0); + + if (fcntl(fd, F_SETFD, v) < 0) + return -1; +#endif + + return 0; +} + +/* Try to parse a boolean string value.*/ +int pa_parse_boolean(const char *v) { + + if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) + return 1; + else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off")) + return 0; + + return -1; +} + +/* Split the specified string wherever one of the strings in delimiter + * occurs. Each time it is called returns a newly allocated string + * with pa_xmalloc(). The variable state points to, should be + * initiallized to NULL before the first call. */ +char *pa_split(const char *c, const char *delimiter, const char**state) { + const char *current = *state ? *state : c; + size_t l; + + if (!*current) + return NULL; + + l = strcspn(current, delimiter); + *state = current+l; + + if (**state) + (*state)++; + + return pa_xstrndup(current, l); +} + +/* What is interpreted as whitespace? */ +#define WHITESPACE " \t\n" + +/* Split a string into words. Otherwise similar to pa_split(). */ +char *pa_split_spaces(const char *c, const char **state) { + const char *current = *state ? *state : c; + size_t l; + + if (!*current || *c == 0) + return NULL; + + current += strspn(current, WHITESPACE); + l = strcspn(current, WHITESPACE); + + *state = current+l; + + return pa_xstrndup(current, l); +} + +/* Return the name of an UNIX signal. Similar to GNU's strsignal() */ +const char *pa_strsignal(int sig) { + switch(sig) { + case SIGINT: return "SIGINT"; + case SIGTERM: return "SIGTERM"; +#ifdef SIGUSR1 + case SIGUSR1: return "SIGUSR1"; +#endif +#ifdef SIGUSR2 + case SIGUSR2: return "SIGUSR2"; +#endif +#ifdef SIGXCPU + case SIGXCPU: return "SIGXCPU"; +#endif +#ifdef SIGPIPE + case SIGPIPE: return "SIGPIPE"; +#endif +#ifdef SIGCHLD + case SIGCHLD: return "SIGCHLD"; +#endif +#ifdef SIGHUP + case SIGHUP: return "SIGHUP"; +#endif + default: return "UNKNOWN SIGNAL"; + } +} + +#ifdef HAVE_GRP_H + +/* Check whether the specified GID and the group name match */ +static int is_group(gid_t gid, const char *name) { + struct group group, *result = NULL; + long n; + void *data; + int r = -1; + +#ifdef HAVE_GETGRGID_R +#ifdef _SC_GETGR_R_SIZE_MAX + n = sysconf(_SC_GETGR_R_SIZE_MAX); +#else + n = -1; +#endif + if (n < 0) n = 512; + data = pa_xmalloc(n); + + if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) { + pa_log(__FILE__": getgrgid_r(%u): %s", (unsigned)gid, pa_cstrerror(errno)); + goto finish; + } + + r = strcmp(name, result->gr_name) == 0; + +finish: + pa_xfree(data); +#else + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not + * support getgrgid_r. */ + if ((result = getgrgid(gid)) == NULL) { + pa_log(__FILE__": getgrgid(%u): %s", gid, pa_cstrerror(errno)); + goto finish; + } + + r = strcmp(name, result->gr_name) == 0; + +finish: +#endif + + return r; +} + +/* Check the current user is member of the specified group */ +int pa_own_uid_in_group(const char *name, gid_t *gid) { + GETGROUPS_T *gids, tgid; + int n = sysconf(_SC_NGROUPS_MAX); + int r = -1, i; + + assert(n > 0); + + gids = pa_xmalloc(sizeof(GETGROUPS_T)*n); + + if ((n = getgroups(n, gids)) < 0) { + pa_log(__FILE__": getgroups(): %s", pa_cstrerror(errno)); + goto finish; + } + + for (i = 0; i < n; i++) { + if (is_group(gids[i], name) > 0) { + *gid = gids[i]; + r = 1; + goto finish; + } + } + + if (is_group(tgid = getgid(), name) > 0) { + *gid = tgid; + r = 1; + goto finish; + } + + r = 0; + +finish: + + pa_xfree(gids); + return r; +} + +int pa_uid_in_group(uid_t uid, const char *name) { + char *g_buf, *p_buf; + long g_n, p_n; + struct group grbuf, *gr; + char **i; + int r = -1; + + g_n = sysconf(_SC_GETGR_R_SIZE_MAX); + g_buf = pa_xmalloc(g_n); + + p_n = sysconf(_SC_GETPW_R_SIZE_MAX); + p_buf = pa_xmalloc(p_n); + + if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) + goto finish; + + r = 0; + for (i = gr->gr_mem; *i; i++) { + struct passwd pwbuf, *pw; + + if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw) + continue; + + if (pw->pw_uid == uid) { + r = 1; + break; + } + } + +finish: + pa_xfree(g_buf); + pa_xfree(p_buf); + + return r; +} + +#else /* HAVE_GRP_H */ + +int pa_own_uid_in_group(const char *name, gid_t *gid) { + return -1; + +} + +int pa_uid_in_group(uid_t uid, const char *name) { + return -1; +} + +#endif + +/* Lock or unlock a file entirely. + (advisory on UNIX, mandatory on Windows) */ +int pa_lock_fd(int fd, int b) { +#ifdef F_SETLKW + struct flock flock; + + /* Try a R/W lock first */ + + flock.l_type = b ? F_WRLCK : F_UNLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + + if (fcntl(fd, F_SETLKW, &flock) >= 0) + return 0; + + /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */ + if (b && errno == EBADF) { + flock.l_type = F_RDLCK; + if (fcntl(fd, F_SETLKW, &flock) >= 0) + return 0; + } + + pa_log(__FILE__": %slock: %s", !b? "un" : "", + pa_cstrerror(errno)); +#endif + +#ifdef OS_IS_WIN32 + HANDLE h = (HANDLE)_get_osfhandle(fd); + + if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF)) + return 0; + if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF)) + return 0; + + pa_log(__FILE__": %slock failed: 0x%08X", !b ? "un" : "", GetLastError()); +#endif + + return -1; +} + +/* Remove trailing newlines from a string */ +char* pa_strip_nl(char *s) { + assert(s); + + s[strcspn(s, "\r\n")] = 0; + return s; +} + +/* Create a temporary lock file and lock it. */ +int pa_lock_lockfile(const char *fn) { + int fd = -1; + assert(fn); + + for (;;) { + struct stat st; + + if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) { + pa_log(__FILE__": failed to create lock file '%s': %s", fn, + pa_cstrerror(errno)); + goto fail; + } + + if (pa_lock_fd(fd, 1) < 0) { + pa_log(__FILE__": failed to lock file '%s'.", fn); + goto fail; + } + + if (fstat(fd, &st) < 0) { + pa_log(__FILE__": failed to fstat() file '%s'.", fn); + goto fail; + } + + /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */ + if (st.st_nlink >= 1) + break; + + if (pa_lock_fd(fd, 0) < 0) { + pa_log(__FILE__": failed to unlock file '%s'.", fn); + goto fail; + } + + if (close(fd) < 0) { + pa_log(__FILE__": failed to close file '%s'.", fn); + goto fail; + } + + fd = -1; + } + + return fd; + +fail: + + if (fd >= 0) + close(fd); + + return -1; +} + +/* Unlock a temporary lcok file */ +int pa_unlock_lockfile(const char *fn, int fd) { + int r = 0; + assert(fn && fd >= 0); + + if (unlink(fn) < 0) { + pa_log_warn(__FILE__": WARNING: unable to remove lock file '%s': %s", + fn, pa_cstrerror(errno)); + r = -1; + } + + if (pa_lock_fd(fd, 0) < 0) { + pa_log_warn(__FILE__": WARNING: failed to unlock file '%s'.", fn); + r = -1; + } + + if (close(fd) < 0) { + pa_log_warn(__FILE__": WARNING: failed to close lock file '%s': %s", + fn, pa_cstrerror(errno)); + r = -1; + } + + return r; +} + +/* Try to open a configuration file. If "env" is specified, open the + * value of the specified environment variable. Otherwise look for a + * file "local" in the home directory or a file "global" in global + * file system. If "result" is non-NULL, a pointer to a newly + * allocated buffer containing the used configuration file is + * stored there.*/ +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) { + const char *fn; + char h[PATH_MAX]; + +#ifdef OS_IS_WIN32 + char buf[PATH_MAX]; + + if (!getenv(POLYP_ROOTENV)) + pa_set_root(NULL); +#endif + + if (env && (fn = getenv(env))) { +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) + return NULL; + fn = buf; +#endif + + if (result) + *result = pa_xstrdup(fn); + + return fopen(fn, mode); + } + + if (local && pa_get_home_dir(h, sizeof(h))) { + FILE *f; + char *lfn; + + fn = lfn = pa_sprintf_malloc("%s/%s", h, local); + +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) + return NULL; + fn = buf; +#endif + + f = fopen(fn, mode); + + if (f || errno != ENOENT) { + if (result) + *result = pa_xstrdup(fn); + pa_xfree(lfn); + return f; + } + + pa_xfree(lfn); + } + + if (!global) { + if (result) + *result = NULL; + errno = ENOENT; + return NULL; + } + +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) + return NULL; + global = buf; +#endif + + if (result) + *result = pa_xstrdup(global); + + return fopen(global, mode); +} + +/* Format the specified data as a hexademical string */ +char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) { + size_t i = 0, j = 0; + const char hex[] = "0123456789abcdef"; + assert(d && s && slength > 0); + + while (i < dlength && j+3 <= slength) { + s[j++] = hex[*d >> 4]; + s[j++] = hex[*d & 0xF]; + + d++; + i++; + } + + s[j < slength ? j : slength] = 0; + return s; +} + +/* Convert a hexadecimal digit to a number or -1 if invalid */ +static int hexc(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + return -1; +} + +/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */ +size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) { + size_t j = 0; + assert(p && d); + + while (j < dlength && *p) { + int b; + + if ((b = hexc(*(p++))) < 0) + return (size_t) -1; + + d[j] = (uint8_t) (b << 4); + + if (!*p) + return (size_t) -1; + + if ((b = hexc(*(p++))) < 0) + return (size_t) -1; + + d[j] |= (uint8_t) b; + j++; + } + + return j; +} + +/* Returns nonzero when *s starts with *pfx */ +int pa_startswith(const char *s, const char *pfx) { + size_t l; + + assert(s); + assert(pfx); + + l = strlen(pfx); + + return strlen(s) >= l && strncmp(s, pfx, l) == 0; +} + +/* Returns nonzero when *s ends with *sfx */ +int pa_endswith(const char *s, const char *sfx) { + size_t l1, l2; + + assert(s); + assert(sfx); + + l1 = strlen(s); + l2 = strlen(sfx); + + return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0; +} + +/* if fn is null return the pulseaudio run time path in s (/tmp/pulseaudio) + * if fn is non-null and starts with / return fn in s + * otherwise append fn to the run time path and return it in s */ +char *pa_runtime_path(const char *fn, char *s, size_t l) { + char u[256]; + +#ifndef OS_IS_WIN32 + if (fn && *fn == '/') +#else + if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\') +#endif + return pa_strlcpy(s, fn, l); + + if (fn) + snprintf(s, l, "%s%s%c%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn); + else + snprintf(s, l, "%s%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u))); + +#ifdef OS_IS_WIN32 + { + char buf[l]; + strcpy(buf, s); + ExpandEnvironmentStrings(buf, s, l); + } +#endif + + return s; +} + +/* Convert the string s to a signed integer in *ret_i */ +int pa_atoi(const char *s, int32_t *ret_i) { + char *x = NULL; + long l; + assert(s && ret_i); + + l = strtol(s, &x, 0); + + if (!x || *x) + return -1; + + *ret_i = (int32_t) l; + + return 0; +} + +/* Convert the string s to an unsigned integer in *ret_u */ +int pa_atou(const char *s, uint32_t *ret_u) { + char *x = NULL; + unsigned long l; + assert(s && ret_u); + + l = strtoul(s, &x, 0); + + if (!x || *x) + return -1; + + *ret_u = (uint32_t) l; + + return 0; +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h new file mode 100644 index 00000000..0012afc6 --- /dev/null +++ b/src/pulsecore/core-util.h @@ -0,0 +1,88 @@ +#ifndef foocoreutilhfoo +#define foocoreutilhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> + +#include <pulsecore/gccmacro.h> + +struct timeval; + +void pa_make_nonblock_fd(int fd); + +int pa_make_secure_dir(const char* dir); +int pa_make_secure_parent_dir(const char *fn); + +ssize_t pa_read(int fd, void *buf, size_t count); +ssize_t pa_write(int fd, const void *buf, size_t count); +ssize_t pa_loop_read(int fd, void*data, size_t size); +ssize_t pa_loop_write(int fd, const void*data, size_t size); + +void pa_check_signal_is_blocked(int sig); + +char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +char *pa_vsprintf_malloc(const char *format, va_list ap); + +char *pa_strlcpy(char *b, const char *s, size_t l); + +char *pa_parent_dir(const char *fn); + +void pa_raise_priority(void); +void pa_reset_priority(void); + +int pa_fd_set_cloexec(int fd, int b); + +int pa_parse_boolean(const char *s); + +char *pa_split(const char *c, const char*delimiters, const char **state); +char *pa_split_spaces(const char *c, const char **state); + +char *pa_strip_nl(char *s); + +const char *pa_strsignal(int sig); + +int pa_own_uid_in_group(const char *name, gid_t *gid); +int pa_uid_in_group(uid_t uid, const char *name); + +int pa_lock_fd(int fd, int b); + +int pa_lock_lockfile(const char *fn); +int pa_unlock_lockfile(const char *fn, int fd); + +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode); + +char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength); +size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength); + +int pa_startswith(const char *s, const char *pfx); +int pa_endswith(const char *s, const char *sfx); + +char *pa_runtime_path(const char *fn, char *s, size_t l); + +int pa_atoi(const char *s, int32_t *ret_i); +int pa_atou(const char *s, uint32_t *ret_u); + +#endif diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c new file mode 100644 index 00000000..7c780ea8 --- /dev/null +++ b/src/pulsecore/core.c @@ -0,0 +1,160 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> +#include <signal.h> + +#include <pulse/timeval.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/module.h> +#include <pulsecore/sink.h> +#include <pulsecore/source.h> +#include <pulsecore/namereg.h> +#include <pulsecore/core-util.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/autoload.h> +#include <pulsecore/core-subscribe.h> +#include <pulsecore/props.h> +#include <pulsecore/random.h> + +#include "core.h" + +pa_core* pa_core_new(pa_mainloop_api *m) { + pa_core* c; + c = pa_xmalloc(sizeof(pa_core)); + + c->mainloop = m; + c->clients = pa_idxset_new(NULL, NULL); + c->sinks = pa_idxset_new(NULL, NULL); + c->sources = pa_idxset_new(NULL, NULL); + c->source_outputs = pa_idxset_new(NULL, NULL); + c->sink_inputs = pa_idxset_new(NULL, NULL); + + c->default_source_name = c->default_sink_name = NULL; + + c->modules = NULL; + c->namereg = NULL; + c->scache = NULL; + c->autoload_idxset = NULL; + c->autoload_hashmap = NULL; + c->running_as_daemon = 0; + + c->default_sample_spec.format = PA_SAMPLE_S16NE; + c->default_sample_spec.rate = 44100; + c->default_sample_spec.channels = 2; + + c->module_auto_unload_event = NULL; + c->module_defer_unload_event = NULL; + c->scache_auto_unload_event = NULL; + + c->subscription_defer_event = NULL; + c->subscription_event_queue = NULL; + c->subscriptions = NULL; + + c->memblock_stat = pa_memblock_stat_new(); + + c->disallow_module_loading = 0; + + c->quit_event = NULL; + + c->exit_idle_time = -1; + c->module_idle_time = 20; + c->scache_idle_time = 20; + + c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST; + + pa_property_init(c); + + pa_random(&c->cookie, sizeof(c->cookie)); + +#ifdef SIGPIPE + pa_check_signal_is_blocked(SIGPIPE); +#endif + return c; +} + +void pa_core_free(pa_core *c) { + assert(c); + + pa_module_unload_all(c); + assert(!c->modules); + + assert(pa_idxset_isempty(c->clients)); + pa_idxset_free(c->clients, NULL, NULL); + + assert(pa_idxset_isempty(c->sinks)); + pa_idxset_free(c->sinks, NULL, NULL); + + assert(pa_idxset_isempty(c->sources)); + pa_idxset_free(c->sources, NULL, NULL); + + assert(pa_idxset_isempty(c->source_outputs)); + pa_idxset_free(c->source_outputs, NULL, NULL); + + assert(pa_idxset_isempty(c->sink_inputs)); + pa_idxset_free(c->sink_inputs, NULL, NULL); + + pa_scache_free(c); + pa_namereg_free(c); + pa_autoload_free(c); + pa_subscription_free_all(c); + + if (c->quit_event) + c->mainloop->time_free(c->quit_event); + + pa_xfree(c->default_source_name); + pa_xfree(c->default_sink_name); + + pa_memblock_stat_unref(c->memblock_stat); + + pa_property_cleanup(c); + + pa_xfree(c); +} + +static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + pa_core *c = userdata; + assert(c->quit_event = e); + + m->quit(m, 0); +} + +void pa_core_check_quit(pa_core *c) { + assert(c); + + if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) { + struct timeval tv; + pa_gettimeofday(&tv); + tv.tv_sec+= c->exit_idle_time; + c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c); + } else if (c->quit_event && pa_idxset_size(c->clients) > 0) { + c->mainloop->time_free(c->quit_event); + c->quit_event = NULL; + } +} + diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h new file mode 100644 index 00000000..627d4239 --- /dev/null +++ b/src/pulsecore/core.h @@ -0,0 +1,82 @@ +#ifndef foocorehfoo +#define foocorehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_core pa_core; + +#include <pulsecore/idxset.h> +#include <pulsecore/hashmap.h> +#include <pulse/mainloop-api.h> +#include <pulse/sample.h> +#include <pulsecore/memblock.h> +#include <pulsecore/resampler.h> +#include <pulsecore/queue.h> +#include <pulsecore/core-subscribe.h> + +/* The core structure of pulseaudio. Every pulseaudio daemon contains + * exactly one of these. It is used for storing kind of global + * variables for the daemon. */ + +struct pa_core { + /* A random value which may be used to identify this instance of + * pulseaudio. Not cryptographically secure in any way. */ + uint32_t cookie; + + pa_mainloop_api *mainloop; + + /* idxset of all kinds of entities */ + pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset; + + /* Some hashmaps for all sorts of entities */ + pa_hashmap *namereg, *autoload_hashmap, *properties; + + /* The name of the default sink/source */ + char *default_source_name, *default_sink_name; + + pa_sample_spec default_sample_spec; + pa_time_event *module_auto_unload_event; + pa_defer_event *module_defer_unload_event; + + pa_defer_event *subscription_defer_event; + pa_queue *subscription_event_queue; + pa_subscription *subscriptions; + + pa_memblock_stat *memblock_stat; + + int disallow_module_loading, running_as_daemon; + int exit_idle_time, module_idle_time, scache_idle_time; + + pa_time_event *quit_event; + + pa_time_event *scache_auto_unload_event; + + pa_resample_method_t resample_method; +}; + +pa_core* pa_core_new(pa_mainloop_api *m); +void pa_core_free(pa_core*c); + +/* Check whether noone is connected to this core */ +void pa_core_check_quit(pa_core *c); + +#endif diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c new file mode 100644 index 00000000..b86bf04f --- /dev/null +++ b/src/pulsecore/dllmain.c @@ -0,0 +1,55 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef OS_IS_WIN32 + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <windows.h> + +extern pa_set_root(HANDLE handle); + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { + WSADATA data; + + switch (fdwReason) { + + case DLL_PROCESS_ATTACH: + if (!pa_set_root(hinstDLL)) + return FALSE; + WSAStartup(MAKEWORD(2, 0), &data); + break; + + case DLL_PROCESS_DETACH: + WSACleanup(); + break; + + } + return TRUE; +} + +#endif /* OS_IS_WIN32 */ diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c new file mode 100644 index 00000000..d1ab1161 --- /dev/null +++ b/src/pulsecore/dynarray.c @@ -0,0 +1,103 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <assert.h> +#include <stdlib.h> + +#include <pulse/xmalloc.h> + +#include "dynarray.h" + +/* If the array becomes to small, increase its size by 100 entries */ +#define INCREASE_BY 100 + +struct pa_dynarray { + void **data; + unsigned n_allocated, n_entries; +}; + +pa_dynarray* pa_dynarray_new(void) { + pa_dynarray *a; + a = pa_xnew(pa_dynarray, 1); + a->data = NULL; + a->n_entries = 0; + a->n_allocated = 0; + return a; +} + +void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) { + unsigned i; + assert(a); + + if (func) + for (i = 0; i < a->n_entries; i++) + if (a->data[i]) + func(a->data[i], userdata); + + pa_xfree(a->data); + pa_xfree(a); +} + +void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) { + assert(a); + + if (i >= a->n_allocated) { + unsigned n; + + if (!p) + return; + + n = i+INCREASE_BY; + a->data = pa_xrealloc(a->data, sizeof(void*)*n); + memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated)); + a->n_allocated = n; + } + + a->data[i] = p; + + if (i >= a->n_entries) + a->n_entries = i+1; +} + +unsigned pa_dynarray_append(pa_dynarray*a, void *p) { + unsigned i = a->n_entries; + pa_dynarray_put(a, i, p); + return i; +} + +void *pa_dynarray_get(pa_dynarray*a, unsigned i) { + assert(a); + if (i >= a->n_allocated) + return NULL; + + assert(a->data); + return a->data[i]; +} + +unsigned pa_dynarray_size(pa_dynarray*a) { + assert(a); + return a->n_entries; +} diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h new file mode 100644 index 00000000..4ddb526c --- /dev/null +++ b/src/pulsecore/dynarray.h @@ -0,0 +1,49 @@ +#ifndef foodynarrayhfoo +#define foodynarrayhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_dynarray pa_dynarray; + +/* Implementation of a simple dynamically sized array. The array + * expands if required, but doesn't shrink if possible. Memory + * management of the array's entries is the user's job. */ + +pa_dynarray* pa_dynarray_new(void); + +/* Free the array calling the specified function for every entry in + * the array. The function may be NULL. */ +void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata); + +/* Store p at position i in the array */ +void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p); + +/* Store p a the first free position in the array. Returns the index + * of that entry. If entries are removed from the array their position + * are not filled any more by this function. */ +unsigned pa_dynarray_append(pa_dynarray*a, void *p); + +void *pa_dynarray_get(pa_dynarray*a, unsigned i); + +unsigned pa_dynarray_size(pa_dynarray*a); + +#endif diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h new file mode 100644 index 00000000..65db3feb --- /dev/null +++ b/src/pulsecore/endianmacros.h @@ -0,0 +1,77 @@ +#ifndef fooendianmacroshfoo +#define fooendianmacroshfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) +#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) +#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) +#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) + +#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x) +#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x) + +#ifdef WORDS_BIGENDIAN + #define INT16_FROM_LE(x) INT16_SWAP(x) + #define INT16_FROM_BE(x) ((int16_t)(x)) + + #define INT16_TO_LE(x) INT16_SWAP(x) + #define INT16_TO_BE(x) ((int16_t)(x)) + + #define UINT16_FROM_LE(x) UINT16_SWAP(x) + #define UINT16_FROM_BE(x) ((uint16_t)(x)) + + #define INT32_FROM_LE(x) INT32_SWAP(x) + #define INT32_FROM_BE(x) ((int32_t)(x)) + + #define UINT32_FROM_LE(x) UINT32_SWAP(x) + #define UINT32_FROM_BE(x) ((uint32_t)(x)) + + #define UINT32_TO_LE(x) UINT32_SWAP(x) + #define UINT32_TO_BE(x) ((uint32_t)(x)) +#else + #define INT16_FROM_LE(x) ((int16_t)(x)) + #define INT16_FROM_BE(x) INT16_SWAP(x) + + #define INT16_TO_LE(x) ((int16_t)(x)) + #define INT16_TO_BE(x) INT16_SWAP(x) + + #define UINT16_FROM_LE(x) ((uint16_t)(x)) + #define UINT16_FROM_BE(x) UINT16_SWAP(x) + + #define INT32_FROM_LE(x) ((int32_t)(x)) + #define INT32_FROM_BE(x) INT32_SWAP(x) + + #define UINT32_FROM_LE(x) ((uint32_t)(x)) + #define UINT32_FROM_BE(x) UINT32_SWAP(x) + + #define UINT32_TO_LE(x) ((uint32_t)(x)) + #define UINT32_TO_BE(x) UINT32_SWAP(x) +#endif + +#endif diff --git a/src/pulsecore/esound.h b/src/pulsecore/esound.h new file mode 100644 index 00000000..9d44f65c --- /dev/null +++ b/src/pulsecore/esound.h @@ -0,0 +1,209 @@ +#ifndef fooesoundhfoo +#define fooesoundhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Most of the following is blatantly stolen from esound. */ + + +/* path and name of the default EsounD domain socket */ +#define ESD_UNIX_SOCKET_DIR "/tmp/.esd" +#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket" + +/* length of the audio buffer size */ +#define ESD_BUF_SIZE (4 * 1024) +/* maximum size we can write(). Otherwise we might overflow */ +#define ESD_MAX_WRITE_SIZE (21 * 4096) + +/* length of the authorization key, octets */ +#define ESD_KEY_LEN (16) + +/* default port for the EsounD server */ +#define ESD_DEFAULT_PORT (16001) + +/* default sample rate for the EsounD server */ +#define ESD_DEFAULT_RATE (44100) + +/* maximum length of a stream/sample name */ +#define ESD_NAME_MAX (128) + +/* a magic number to identify the relative endianness of a client */ +#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N'))) + +#define ESD_VOLUME_BASE (256) + + +/*************************************/ +/* what can we do to/with the EsounD */ +enum esd_proto { + ESD_PROTO_CONNECT, /* implied on inital client connection */ + + /* pseudo "security" functionality */ + ESD_PROTO_LOCK, /* disable "foreign" client connections */ + ESD_PROTO_UNLOCK, /* enable "foreign" client connections */ + + /* stream functionality: play, record, monitor */ + ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */ + ESD_PROTO_STREAM_REC, /* record data from card as a stream */ + ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */ + + /* sample functionality: cache, free, play, loop, EOL, kill */ + ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */ + ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */ + ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */ + ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */ + ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */ + ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */ + + /* free and reclaim /dev/dsp functionality */ + ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */ + ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */ + + /* TODO: move these to a more logical place. NOTE: will break the protocol */ + ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */ + ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */ + + /* esd remote management */ + ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */ + ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */ + ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */ + ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */ + + /* esd remote control */ + ESD_PROTO_STREAM_PAN, /* set stream panning */ + ESD_PROTO_SAMPLE_PAN, /* set default sample panning */ + + /* esd status */ + ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */ + + /* esd latency */ + ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */ + + ESD_PROTO_MAX /* for bounds checking */ +}; + +/******************/ +/* The EsounD api */ + +/* the properties of a sound buffer are logically or'd */ + +/* bits of stream/sample data */ +#define ESD_MASK_BITS ( 0x000F ) +#define ESD_BITS8 ( 0x0000 ) +#define ESD_BITS16 ( 0x0001 ) + +/* how many interleaved channels of data */ +#define ESD_MASK_CHAN ( 0x00F0 ) +#define ESD_MONO ( 0x0010 ) +#define ESD_STEREO ( 0x0020 ) + +/* whether it's a stream or a sample */ +#define ESD_MASK_MODE ( 0x0F00 ) +#define ESD_STREAM ( 0x0000 ) +#define ESD_SAMPLE ( 0x0100 ) +#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */ + +/* the function of the stream/sample, and common functions */ +#define ESD_MASK_FUNC ( 0xF000 ) +#define ESD_PLAY ( 0x1000 ) +/* functions for streams only */ +#define ESD_MONITOR ( 0x0000 ) +#define ESD_RECORD ( 0x2000 ) +/* functions for samples only */ +#define ESD_STOP ( 0x0000 ) +#define ESD_LOOP ( 0x2000 ) + +typedef int esd_format_t; +typedef int esd_proto_t; + +/*******************************************************************/ +/* esdmgr.c - functions to implement a "sound manager" for esd */ + +/* structures to retrieve information about streams/samples from the server */ +typedef struct esd_server_info { + + int version; /* server version encoded as an int */ + esd_format_t format; /* magic int with the format info */ + int rate; /* sample rate */ + +} esd_server_info_t; + +typedef struct esd_player_info { + + struct esd_player_info *next; /* point to next entry in list */ + esd_server_info_t *server; /* the server that contains this stream */ + + int source_id; /* either a stream fd or sample id */ + char name[ ESD_NAME_MAX ]; /* name of stream for remote control */ + int rate; /* sample rate */ + int left_vol_scale; /* volume scaling */ + int right_vol_scale; + + esd_format_t format; /* magic int with the format info */ + +} esd_player_info_t; + +typedef struct esd_sample_info { + + struct esd_sample_info *next; /* point to next entry in list */ + esd_server_info_t *server; /* the server that contains this sample */ + + int sample_id; /* either a stream fd or sample id */ + char name[ ESD_NAME_MAX ]; /* name of stream for remote control */ + int rate; /* sample rate */ + int left_vol_scale; /* volume scaling */ + int right_vol_scale; + + esd_format_t format; /* magic int with the format info */ + int length; /* total buffer length */ + +} esd_sample_info_t; + +typedef struct esd_info { + + esd_server_info_t *server; + esd_player_info_t *player_list; + esd_sample_info_t *sample_list; + +} esd_info_t; + +enum esd_standby_mode { + ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING +}; +typedef int esd_standby_mode_t; + +enum esd_client_state { + ESD_STREAMING_DATA, /* data from here on is streamed data */ + ESD_CACHING_SAMPLE, /* midway through caching a sample */ + ESD_NEEDS_REQDATA, /* more data needed to complere request */ + ESD_NEXT_REQUEST, /* proceed to next request */ + ESD_CLIENT_STATE_MAX /* place holder */ +}; +typedef int esd_client_state_t; + +/* the endian key is transferred in binary, if it's read into int, */ +/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */ +/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */ +#define ESD_SWAP_ENDIAN_KEY (UINT32_SWAP(ESD_ENDIAN_KEY)) + + +#endif diff --git a/src/pulsecore/g711.c b/src/pulsecore/g711.c new file mode 100644 index 00000000..55a82396 --- /dev/null +++ b/src/pulsecore/g711.c @@ -0,0 +1,2531 @@ +/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+
+#include "g711.h"
+
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)
+static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+static int16_t search(
+ int16_t val,
+ int16_t *table,
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+#endif /* !FAST_*_CONVERSION */
+
+#ifndef FAST_ALAW_CONVERSION
+/*
+ * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data
+ * stored in a unsigned char. This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 13-bits.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_13linear2alaw(
+ int16_t pcm_val) /* 2's complement (13-bit range) */
+{
+ int16_t mask;
+ short seg;
+ unsigned char aval;
+
+ /* Have calling software do it since its already doing a shift
+ * from 32-bits down to 16-bits.
+ */
+ /* pcm_val = pcm_val >> 3; */
+
+ /* A-law using even bit inversion */
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ aval = (unsigned char) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM
+ *
+ */
+int16_t st_alaw2linear16(
+ unsigned char a_val)
+{
+ int16_t t;
+ int16_t seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+#endif /* !FAST_ALAW_CONVERSION */
+
+#define BIAS (0x84) /* Bias for linear code. */
+#define CLIP 8159
+
+#ifndef FAST_ULAW_CONVERSION
+/*
+ * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data
+ * stored in a unsigned char. This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 14-bits.
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_14linear2ulaw(
+ int16_t pcm_val) /* 2's complement (14-bit range) */
+{
+ int16_t mask;
+ int16_t seg;
+ unsigned char uval;
+
+ /* Have calling software do it since its already doing a shift
+ * from 32-bits down to 16-bits.
+ */
+ /* pcm_val = pcm_val >> 2; */
+
+ /* u-law inverts all bits */
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+int16_t st_ulaw2linear16(
+ unsigned char u_val)
+{
+ int16_t t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+#endif /* !FAST_ULAW_CONVERSION */
+
+#ifdef FAST_ALAW_CONVERSION
+
+int16_t _st_alaw2linear16[256] = {
+ -5504, -5248, -6016, -5760, -4480, -4224, -4992,
+ -4736, -7552, -7296, -8064, -7808, -6528, -6272,
+ -7040, -6784, -2752, -2624, -3008, -2880, -2240,
+ -2112, -2496, -2368, -3776, -3648, -4032, -3904,
+ -3264, -3136, -3520, -3392, -22016, -20992, -24064,
+ -23040, -17920, -16896, -19968, -18944, -30208, -29184,
+ -32256, -31232, -26112, -25088, -28160, -27136, -11008,
+ -10496, -12032, -11520, -8960, -8448, -9984, -9472,
+ -15104, -14592, -16128, -15616, -13056, -12544, -14080,
+ -13568, -344, -328, -376, -360, -280, -264,
+ -312, -296, -472, -456, -504, -488, -408,
+ -392, -440, -424, -88, -72, -120, -104,
+ -24, -8, -56, -40, -216, -200, -248,
+ -232, -152, -136, -184, -168, -1376, -1312,
+ -1504, -1440, -1120, -1056, -1248, -1184, -1888,
+ -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624,
+ -592, -944, -912, -1008, -976, -816, -784,
+ -880, -848, 5504, 5248, 6016, 5760, 4480,
+ 4224, 4992, 4736, 7552, 7296, 8064, 7808,
+ 6528, 6272, 7040, 6784, 2752, 2624, 3008,
+ 2880, 2240, 2112, 2496, 2368, 3776, 3648,
+ 4032, 3904, 3264, 3136, 3520, 3392, 22016,
+ 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160,
+ 27136, 11008, 10496, 12032, 11520, 8960, 8448,
+ 9984, 9472, 15104, 14592, 16128, 15616, 13056,
+ 12544, 14080, 13568, 344, 328, 376, 360,
+ 280, 264, 312, 296, 472, 456, 504,
+ 488, 408, 392, 440, 424, 88, 72,
+ 120, 104, 24, 8, 56, 40, 216,
+ 200, 248, 232, 152, 136, 184, 168,
+ 1376, 1312, 1504, 1440, 1120, 1056, 1248,
+ 1184, 1888, 1824, 2016, 1952, 1632, 1568,
+ 1760, 1696, 688, 656, 752, 720, 560,
+ 528, 624, 592, 944, 912, 1008, 976,
+ 816, 784, 880, 848
+};
+
+uint8_t _st_13linear2alaw[0x2000] = {
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b,
+ 0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
+ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,
+ 0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,
+ 0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
+ 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+ 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73,
+ 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76,
+ 0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
+ 0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f,
+ 0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41,
+ 0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d,
+ 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57,
+ 0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6,
+ 0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc,
+ 0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda,
+ 0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0,
+ 0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce,
+ 0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+#endif /* FAST_ALAW_CONVERSION */
+
+#ifdef FAST_ULAW_CONVERSION
+
+int16_t _st_ulaw2linear16[256] = {
+ -32124, -31100, -30076, -29052, -28028, -27004, -25980,
+ -24956, -23932, -22908, -21884, -20860, -19836, -18812,
+ -17788, -16764, -15996, -15484, -14972, -14460, -13948,
+ -13436, -12924, -12412, -11900, -11388, -10876, -10364,
+ -9852, -9340, -8828, -8316, -7932, -7676, -7420,
+ -7164, -6908, -6652, -6396, -6140, -5884, -5628,
+ -5372, -5116, -4860, -4604, -4348, -4092, -3900,
+ -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108,
+ -1980, -1884, -1820, -1756, -1692, -1628, -1564,
+ -1500, -1436, -1372, -1308, -1244, -1180, -1116,
+ -1052, -988, -924, -876, -844, -812, -780,
+ -748, -716, -684, -652, -620, -588, -556,
+ -524, -492, -460, -428, -396, -372, -356,
+ -340, -324, -308, -292, -276, -260, -244,
+ -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72,
+ -64, -56, -48, -40, -32, -24, -16,
+ -8, 0, 32124, 31100, 30076, 29052, 28028,
+ 27004, 25980, 24956, 23932, 22908, 21884, 20860,
+ 19836, 18812, 17788, 16764, 15996, 15484, 14972,
+ 14460, 13948, 13436, 12924, 12412, 11900, 11388,
+ 10876, 10364, 9852, 9340, 8828, 8316, 7932,
+ 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348,
+ 4092, 3900, 3772, 3644, 3516, 3388, 3260,
+ 3132, 3004, 2876, 2748, 2620, 2492, 2364,
+ 2236, 2108, 1980, 1884, 1820, 1756, 1692,
+ 1628, 1564, 1500, 1436, 1372, 1308, 1244,
+ 1180, 1116, 1052, 988, 924, 876, 844,
+ 812, 780, 748, 716, 684, 652, 620,
+ 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276,
+ 260, 244, 228, 212, 196, 180, 164,
+ 148, 132, 120, 112, 104, 96, 88,
+ 80, 72, 64, 56, 48, 40, 32,
+ 24, 16, 8, 0
+};
+
+uint8_t _st_14linear2ulaw[0x4000] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43,
+ 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
+ 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+ 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46,
+ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
+ 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
+ 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
+ 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b,
+ 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d,
+ 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51,
+ 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,
+ 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54,
+ 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
+ 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+ 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+ 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63,
+ 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66,
+ 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c,
+ 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+ 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd,
+ 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7,
+ 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1,
+ 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed,
+ 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea,
+ 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
+ 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80
+};
+
+#endif /* FAST_ULAW_CONVERSION */
+
+/* The following code was used to generate the lookup tables */
+#if 0
+int main()
+{
+ int x, y, find2a = 0;
+
+ y = 0;
+ printf("int16_t _st_alaw2linear16[256] = {\n ");
+ for (x = 0; x < 256; x++)
+ {
+ printf("%8d,", st_alaw2linear16(x));
+ y++;
+ if (y == 7)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n ");
+ y = 0;
+ for (x = 0; x < 0x2000; x++)
+ {
+ printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));
+ y++;
+ if (y == 12)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n ");
+ y = 0;
+ for (x = 0; x < 256; x++)
+ {
+ printf("%8d,", st_ulaw2linear16(x));
+ y++;
+ if (y == 7)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n ");
+ y = 0;
+ for (x = 0; x < 0x4000; x++)
+ {
+ printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));
+ y++;
+ if (y == 12)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+ printf("\n};\n");
+
+}
+#endif
+
+/* The following is not used by SoX but kept for reference */
+#if 0
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+/* corrected:
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ should be: */
+ 80, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+/* corrected:
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ should be: */
+ 73, 74, 75, 76, 77, 78, 79, 80,
+
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+/* A-law to u-law conversion */
+unsigned char st_alaw2ulaw(
+ unsigned char aval)
+{
+ aval &= 0xff;
+ return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char st_ulaw2alaw(
+ unsigned char uval)
+{
+ uval &= 0xff;
+ return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+#endif
diff --git a/src/pulsecore/g711.h b/src/pulsecore/g711.h new file mode 100644 index 00000000..97cedf81 --- /dev/null +++ b/src/pulsecore/g711.h @@ -0,0 +1,40 @@ +#ifndef foog711hfoo +#define foog711hfoo + +/* g711.h - include for G711 u-law and a-law conversion routines +** +** Copyright (C) 2001 Chris Bagwell +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +/** Copied from sox -- Lennart Poettring*/ + +#include <inttypes.h> + +#ifdef FAST_ALAW_CONVERSION +extern uint8_t _st_13linear2alaw[0x2000]; +extern int16_t _st_alaw2linear16[256]; +#define st_13linear2alaw(sw) (_st_13linear2alaw[(sw + 0x1000)]) +#define st_alaw2linear16(uc) (_st_alaw2linear16[uc]) +#else +unsigned char st_13linear2alaw(int16_t pcm_val); +int16_t st_alaw2linear16(unsigned char); +#endif + +#ifdef FAST_ULAW_CONVERSION +extern uint8_t _st_14linear2ulaw[0x4000]; +extern int16_t _st_ulaw2linear16[256]; +#define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)]) +#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc]) +#else +unsigned char st_14linear2ulaw(int16_t pcm_val); +int16_t st_ulaw2linear16(unsigned char); +#endif + +#endif diff --git a/src/pulsecore/gccmacro.h b/src/pulsecore/gccmacro.h new file mode 100644 index 00000000..2ab999d9 --- /dev/null +++ b/src/pulsecore/gccmacro.h @@ -0,0 +1,53 @@ +#ifndef foogccmacrohfoo +#define foogccmacrohfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef __GNUC__ +#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) +#else +/** If we're in GNU C, use some magic for detecting invalid format strings */ +#define PA_GCC_PRINTF_ATTR(a,b) +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define PA_GCC_SENTINEL __attribute__ ((sentinel)) +#else +/** Macro for usage of GCC's sentinel compilation warnings */ +#define PA_GCC_SENTINEL +#endif + +#ifdef __GNUC__ +#define PA_GCC_NORETURN __attribute__((noreturn)) +#else +/** Macro for no-return functions */ +#define PA_GCC_NORETURN +#endif + +#ifdef __GNUC__ +#define PA_GCC_UNUSED __attribute__ ((unused)) +#else +/** Macro for not used parameter */ +#define PA_GCC_UNUSED +#endif + +#endif diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c new file mode 100644 index 00000000..2cddba1d --- /dev/null +++ b/src/pulsecore/hashmap.c @@ -0,0 +1,196 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/idxset.h> +#include <pulsecore/log.h> + +#include "hashmap.h" + +#define BUCKETS 1023 + +struct hashmap_entry { + struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous; + unsigned hash; + const void *key; + void *value; +}; + +struct pa_hashmap { + unsigned size; + struct hashmap_entry **data; + struct hashmap_entry *first_entry; + + unsigned n_entries; + unsigned (*hash_func) (const void *p); + int (*compare_func) (const void*a, const void*b); +}; + +pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { + pa_hashmap *h; + h = pa_xmalloc(sizeof(pa_hashmap)); + h->data = pa_xmalloc0(sizeof(struct hashmap_entry*)*(h->size = BUCKETS)); + h->first_entry = NULL; + h->n_entries = 0; + h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; + h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; + return h; +} + +static void remove(pa_hashmap *h, struct hashmap_entry *e) { + assert(e); + + if (e->next) + e->next->previous = e->previous; + if (e->previous) + e->previous->next = e->next; + else + h->first_entry = e->next; + + if (e->bucket_next) + e->bucket_next->bucket_previous = e->bucket_previous; + if (e->bucket_previous) + e->bucket_previous->bucket_next = e->bucket_next; + else { + assert(e->hash < h->size); + h->data[e->hash] = e->bucket_next; + } + + pa_xfree(e); + h->n_entries--; +} + +void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) { + assert(h); + + while (h->first_entry) { + if (free_func) + free_func(h->first_entry->value, userdata); + remove(h, h->first_entry); + } + + pa_xfree(h->data); + pa_xfree(h); +} + +static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) { + struct hashmap_entry *e; + assert(h && hash < h->size); + + for (e = h->data[hash]; e; e = e->bucket_next) + if (h->compare_func(e->key, key) == 0) + return e; + + return NULL; +} + +int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + assert(h); + + hash = h->hash_func(key) % h->size; + + if ((e = get(h, hash, key))) + return -1; + + e = pa_xmalloc(sizeof(struct hashmap_entry)); + e->hash = hash; + e->key = key; + e->value = value; + + e->previous = NULL; + e->next = h->first_entry; + if (h->first_entry) + h->first_entry->previous = e; + h->first_entry = e; + + e->bucket_previous = NULL; + e->bucket_next = h->data[hash]; + if (h->data[hash]) + h->data[hash]->bucket_previous = e; + h->data[hash] = e; + + h->n_entries ++; + return 0; +} + +void* pa_hashmap_get(pa_hashmap *h, const void *key) { + unsigned hash; + struct hashmap_entry *e; + assert(h && key); + + hash = h->hash_func(key) % h->size; + + if (!(e = get(h, hash, key))) + return NULL; + + return e->value; +} + +void* pa_hashmap_remove(pa_hashmap *h, const void *key) { + struct hashmap_entry *e; + unsigned hash; + void *data; + assert(h && key); + + hash = h->hash_func(key) % h->size; + + if (!(e = get(h, hash, key))) + return NULL; + + data = e->value; + remove(h, e); + return data; +} + +unsigned pa_hashmap_size(pa_hashmap *h) { + return h->n_entries; +} + +void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) { + assert(h && state); + + if (!*state) + *state = h->first_entry; + else + *state = ((struct hashmap_entry*) *state)->next; + + if (!*state) { + if (key) + *key = NULL; + return NULL; + } + + if (key) + *key = ((struct hashmap_entry*) *state)->key; + + return ((struct hashmap_entry*) *state)->value; +} diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h new file mode 100644 index 00000000..3f62adb1 --- /dev/null +++ b/src/pulsecore/hashmap.h @@ -0,0 +1,53 @@ +#ifndef foohashmaphfoo +#define foohashmaphfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Simple Implementation of a hash table. Memory management is the + * user's job. It's a good idea to have the key pointer point to a + * string in the value data. */ + +typedef struct pa_hashmap pa_hashmap; + +/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */ +pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); + +/* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */ +void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata); + +/* Returns non-zero when the entry already exists */ +int pa_hashmap_put(pa_hashmap *h, const void *key, void *value); +void* pa_hashmap_get(pa_hashmap *h, const void *key); + +/* Returns the data of the entry while removing */ +void* pa_hashmap_remove(pa_hashmap *h, const void *key); + +unsigned pa_hashmap_size(pa_hashmap *h); + +/* May be used to iterate through the hashmap. Initially the opaque + pointer *state has to be set to NULL. The hashmap may not be + modified during iteration. The key of the entry is returned in + *key, if key is non-NULL. After the last entry in the hashmap NULL + is returned. */ +void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key); + +#endif diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c new file mode 100644 index 00000000..ddce623a --- /dev/null +++ b/src/pulsecore/idxset.c @@ -0,0 +1,391 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include "idxset.h" + +typedef struct idxset_entry { + void *data; + uint32_t index; + unsigned hash_value; + + struct idxset_entry *hash_prev, *hash_next; + struct idxset_entry* iterate_prev, *iterate_next; +} idxset_entry; + +struct pa_idxset { + unsigned (*hash_func) (const void *p); + int (*compare_func)(const void *a, const void *b); + + unsigned hash_table_size, n_entries; + idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail; + uint32_t index, start_index, array_size; +}; + +unsigned pa_idxset_string_hash_func(const void *p) { + unsigned hash = 0; + const char *c; + + for (c = p; *c; c++) + hash = 31 * hash + *c; + + return hash; +} + +int pa_idxset_string_compare_func(const void *a, const void *b) { + return strcmp(a, b); +} + +unsigned pa_idxset_trivial_hash_func(const void *p) { + return (unsigned) (long) p; +} + +int pa_idxset_trivial_compare_func(const void *a, const void *b) { + return a != b; +} + +pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { + pa_idxset *s; + + s = pa_xnew(pa_idxset, 1); + s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; + s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; + s->hash_table_size = 1023; + s->hash_table = pa_xmalloc0(sizeof(idxset_entry*)*s->hash_table_size); + s->array = NULL; + s->array_size = 0; + s->index = 0; + s->start_index = 0; + s->n_entries = 0; + + s->iterate_list_head = s->iterate_list_tail = NULL; + + return s; +} + +void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) { + assert(s); + + while (s->iterate_list_head) { + idxset_entry *e = s->iterate_list_head; + s->iterate_list_head = s->iterate_list_head->iterate_next; + + if (free_func) + free_func(e->data, userdata); + pa_xfree(e); + } + + pa_xfree(s->hash_table); + pa_xfree(s->array); + pa_xfree(s); +} + +static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) { + assert(p); + + assert(s->compare_func); + for (; e; e = e->hash_next) + if (s->compare_func(e->data, p) == 0) + return e; + + return NULL; +} + +static void extend_array(pa_idxset *s, uint32_t idx) { + uint32_t i, j, l; + idxset_entry** n; + assert(idx >= s->start_index); + + if (idx < s->start_index + s->array_size) + return; + + for (i = 0; i < s->array_size; i++) + if (s->array[i]) + break; + + l = idx - s->start_index - i + 100; + n = pa_xnew0(idxset_entry*, l); + + for (j = 0; j < s->array_size-i; j++) + n[j] = s->array[i+j]; + + pa_xfree(s->array); + + s->array = n; + s->array_size = l; + s->start_index += i; +} + +static idxset_entry** array_index(pa_idxset*s, uint32_t idx) { + if (idx >= s->start_index + s->array_size) + return NULL; + + if (idx < s->start_index) + return NULL; + + return s->array + (idx - s->start_index); +} + +int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) { + unsigned h; + idxset_entry *e, **a; + assert(s && p); + + assert(s->hash_func); + h = s->hash_func(p) % s->hash_table_size; + + assert(s->hash_table); + if ((e = hash_scan(s, s->hash_table[h], p))) { + if (idx) + *idx = e->index; + + return -1; + } + + e = pa_xmalloc(sizeof(idxset_entry)); + e->data = p; + e->index = s->index++; + e->hash_value = h; + + /* Insert into hash table */ + e->hash_next = s->hash_table[h]; + e->hash_prev = NULL; + if (s->hash_table[h]) + s->hash_table[h]->hash_prev = e; + s->hash_table[h] = e; + + /* Insert into array */ + extend_array(s, e->index); + a = array_index(s, e->index); + assert(a && !*a); + *a = e; + + /* Insert into linked list */ + e->iterate_next = NULL; + e->iterate_prev = s->iterate_list_tail; + if (s->iterate_list_tail) { + assert(s->iterate_list_head); + s->iterate_list_tail->iterate_next = e; + } else { + assert(!s->iterate_list_head); + s->iterate_list_head = e; + } + s->iterate_list_tail = e; + + s->n_entries++; + assert(s->n_entries >= 1); + + if (idx) + *idx = e->index; + + return 0; +} + +void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) { + idxset_entry **a; + assert(s); + + if (!(a = array_index(s, idx))) + return NULL; + + if (!*a) + return NULL; + + return (*a)->data; +} + +void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) { + unsigned h; + idxset_entry *e; + assert(s && p); + + assert(s->hash_func); + h = s->hash_func(p) % s->hash_table_size; + + assert(s->hash_table); + if (!(e = hash_scan(s, s->hash_table[h], p))) + return NULL; + + if (idx) + *idx = e->index; + + return e->data; +} + +static void remove_entry(pa_idxset *s, idxset_entry *e) { + idxset_entry **a; + assert(s && e); + + /* Remove from array */ + a = array_index(s, e->index); + assert(a && *a && *a == e); + *a = NULL; + + /* Remove from linked list */ + if (e->iterate_next) + e->iterate_next->iterate_prev = e->iterate_prev; + else + s->iterate_list_tail = e->iterate_prev; + + if (e->iterate_prev) + e->iterate_prev->iterate_next = e->iterate_next; + else + s->iterate_list_head = e->iterate_next; + + /* Remove from hash table */ + if (e->hash_next) + e->hash_next->hash_prev = e->hash_prev; + + if (e->hash_prev) + e->hash_prev->hash_next = e->hash_next; + else + s->hash_table[e->hash_value] = e->hash_next; + + pa_xfree(e); + + assert(s->n_entries >= 1); + s->n_entries--; +} + +void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) { + idxset_entry **a; + void *data; + + assert(s); + + if (!(a = array_index(s, idx))) + return NULL; + + data = (*a)->data; + remove_entry(s, *a); + + return data; +} + +void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) { + idxset_entry *e; + unsigned h; + void *r; + + assert(s->hash_func); + h = s->hash_func(data) % s->hash_table_size; + + assert(s->hash_table); + if (!(e = hash_scan(s, s->hash_table[h], data))) + return NULL; + + r = e->data; + if (idx) + *idx = e->index; + + remove_entry(s, e); + + return r; +} + +void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) { + idxset_entry **a, *e = NULL; + assert(s && idx); + + if ((a = array_index(s, *idx)) && *a) + e = (*a)->iterate_next; + + if (!e) + e = s->iterate_list_head; + + if (!e) + return NULL; + + *idx = e->index; + return e->data; +} + +void* pa_idxset_first(pa_idxset *s, uint32_t *idx) { + assert(s); + + if (!s->iterate_list_head) + return NULL; + + if (idx) + *idx = s->iterate_list_head->index; + return s->iterate_list_head->data; +} + +void *pa_idxset_next(pa_idxset *s, uint32_t *idx) { + idxset_entry **a, *e = NULL; + assert(s && idx); + + if ((a = array_index(s, *idx)) && *a) + e = (*a)->iterate_next; + + if (e) { + *idx = e->index; + return e->data; + } else { + *idx = PA_IDXSET_INVALID; + return NULL; + } +} + + +int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) { + idxset_entry *e; + assert(s && func); + + e = s->iterate_list_head; + while (e) { + int del = 0, r; + idxset_entry *n = e->iterate_next; + + r = func(e->data, e->index, &del, userdata); + + if (del) + remove_entry(s, e); + + if (r < 0) + return r; + + e = n; + } + + return 0; +} + +unsigned pa_idxset_size(pa_idxset*s) { + assert(s); + return s->n_entries; +} + +int pa_idxset_isempty(pa_idxset *s) { + assert(s); + return s->n_entries == 0; +} + diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h new file mode 100644 index 00000000..3d0bc75f --- /dev/null +++ b/src/pulsecore/idxset.h @@ -0,0 +1,94 @@ +#ifndef fooidxsethfoo +#define fooidxsethfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> + +/* A combination of a set and a dynamic array. Entries are indexable + * both through a numeric automatically generated index and the entry's + * data pointer. As usual, memory management is the user's job. */ + +/* A special index value denoting the invalid index. */ +#define PA_IDXSET_INVALID ((uint32_t) -1) + +/* Generic implementations for hash and comparison functions. Just + * compares the pointer or calculates the hash value directly from the + * pointer value. */ +unsigned pa_idxset_trivial_hash_func(const void *p); +int pa_idxset_trivial_compare_func(const void *a, const void *b); + +/* Generic implementations for hash and comparison functions for strings. */ +unsigned pa_idxset_string_hash_func(const void *p); +int pa_idxset_string_compare_func(const void *a, const void *b); + +typedef struct pa_idxset pa_idxset; + +/* Instantiate a new idxset with the specified hash and comparison functions */ +pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); + +/* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */ +void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata); + +/* Store a new item in the idxset. The index of the item is returned in *idx */ +int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx); + +/* Get the entry by its idx */ +void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx); + +/* Get the entry by its data. The idx is returned in *index */ +void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx); + +/* Similar to pa_idxset_get_by_index(), but removes the entry from the idxset. */ +void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx); + +/* Similar to pa_idxset_get_by_data(), but removes the entry from the idxset */ +void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx); + +/* This may be used to iterate through all entries. When called with + an invalid index value it returns the first entry, otherwise the + next following. The function is best called with *idx = + PA_IDXSET_VALID first. It is safe to manipulate the idxset between + the calls. It is not guaranteed that all entries have already been + returned before the an entry is returned the second time.*/ +void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx); + +/* Return the oldest entry in the idxset. Fill in its index in *idx. */ +void* pa_idxset_first(pa_idxset *s, uint32_t *idx); + +/* Return the entry following the entry indexed by *idx. After the + * call *index contains the index of the returned + * object. pa_idxset_first() and pa_idxset_next() may be used to + * iterate through the set.*/ +void *pa_idxset_next(pa_idxset *s, uint32_t *idx); + +/* Call a function for every item in the set. If the callback function + returns -1, the loop is terminated. If *del is set to non-zero that + specific item is removed. It is not safe to call any other + functions on the idxset while pa_idxset_foreach is executed. */ +int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata); + +unsigned pa_idxset_size(pa_idxset*s); + +int pa_idxset_isempty(pa_idxset *s); + +#endif diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c new file mode 100644 index 00000000..483c3e26 --- /dev/null +++ b/src/pulsecore/inet_ntop.c @@ -0,0 +1,80 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <assert.h> + +#ifndef HAVE_INET_NTOP + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "winsock.h" + +#include "inet_ntop.h" + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { + struct in_addr *in = (struct in_addr*)src; + struct in6_addr *in6 = (struct in6_addr*)src; + + assert(src && dst); + + switch (af) { + case AF_INET: + snprintf(dst, cnt, "%d.%d.%d.%d", +#ifdef WORDS_BIGENDIAN + (int)(in->s_addr >> 24) & 0xff, + (int)(in->s_addr >> 16) & 0xff, + (int)(in->s_addr >> 8) & 0xff, + (int)(in->s_addr >> 0) & 0xff); +#else + (int)(in->s_addr >> 0) & 0xff, + (int)(in->s_addr >> 8) & 0xff, + (int)(in->s_addr >> 16) & 0xff, + (int)(in->s_addr >> 24) & 0xff); +#endif + break; + case AF_INET6: + snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x", + in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1], + in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3], + in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5], + in6->s6_addr[ 6] << 8 | in6->s6_addr[ 7], + in6->s6_addr[ 8] << 8 | in6->s6_addr[ 9], + in6->s6_addr[10] << 8 | in6->s6_addr[11], + in6->s6_addr[12] << 8 | in6->s6_addr[13], + in6->s6_addr[14] << 8 | in6->s6_addr[15]); + break; + default: + errno = EAFNOSUPPORT; + return NULL; + } + + return dst; +} + +#endif /* INET_NTOP */ diff --git a/src/pulsecore/inet_ntop.h b/src/pulsecore/inet_ntop.h new file mode 100644 index 00000000..7fb67b44 --- /dev/null +++ b/src/pulsecore/inet_ntop.h @@ -0,0 +1,12 @@ +#ifndef fooinet_ntophfoo +#define fooinet_ntophfoo + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "winsock.h" + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); + +#endif diff --git a/src/pulsecore/inet_pton.c b/src/pulsecore/inet_pton.c new file mode 100644 index 00000000..7b6bbc31 --- /dev/null +++ b/src/pulsecore/inet_pton.c @@ -0,0 +1,62 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <assert.h> + +#ifndef HAVE_INET_PTON + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "winsock.h" + +#include "inet_pton.h" + +int inet_pton(int af, const char *src, void *dst) { + struct in_addr *in = (struct in_addr*)dst; + struct in6_addr *in6 = (struct in6_addr*)dst; + + assert(src && dst); + + switch (af) { + case AF_INET: + in->s_addr = inet_addr(src); + if (in->s_addr == INADDR_NONE) + return 0; + break; + case AF_INET6: + /* FIXME */ + default: + errno = EAFNOSUPPORT; + return -1; + } + + return 1; +} + +#endif /* INET_PTON */ diff --git a/src/pulsecore/inet_pton.h b/src/pulsecore/inet_pton.h new file mode 100644 index 00000000..111b4a07 --- /dev/null +++ b/src/pulsecore/inet_pton.h @@ -0,0 +1,12 @@ +#ifndef fooinet_ptonhfoo +#define fooinet_ptonhfoo + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "winsock.h" + +int inet_pton(int af, const char *src, void *dst); + +#endif diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c new file mode 100644 index 00000000..8a19094a --- /dev/null +++ b/src/pulsecore/iochannel.c @@ -0,0 +1,417 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + +#include "winsock.h" + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/core-util.h> +#include <pulsecore/socket-util.h> +#include <pulsecore/log.h> + +#include "iochannel.h" + +struct pa_iochannel { + int ifd, ofd; + pa_mainloop_api* mainloop; + + pa_iochannel_cb_t callback; + void*userdata; + + int readable; + int writable; + int hungup; + + int no_close; + + pa_io_event* input_event, *output_event; +}; + +static void enable_mainloop_sources(pa_iochannel *io) { + assert(io); + + if (io->input_event == io->output_event && io->input_event) { + pa_io_event_flags_t f = PA_IO_EVENT_NULL; + assert(io->input_event); + + if (!io->readable) + f |= PA_IO_EVENT_INPUT; + if (!io->writable) + f |= PA_IO_EVENT_OUTPUT; + + io->mainloop->io_enable(io->input_event, f); + } else { + if (io->input_event) + io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT); + if (io->output_event) + io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT); + } +} + +static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { + pa_iochannel *io = userdata; + int changed = 0; + + assert(m); + assert(e); + assert(fd >= 0); + assert(userdata); + + if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) { + io->hungup = 1; + changed = 1; + } + + if ((f & PA_IO_EVENT_INPUT) && !io->readable) { + io->readable = 1; + changed = 1; + assert(e == io->input_event); + } + + if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) { + io->writable = 1; + changed = 1; + assert(e == io->output_event); + } + + if (changed) { + enable_mainloop_sources(io); + + if (io->callback) + io->callback(io, io->userdata); + } +} + +pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) { + pa_iochannel *io; + + assert(m); + assert(ifd >= 0 || ofd >= 0); + + io = pa_xnew(pa_iochannel, 1); + io->ifd = ifd; + io->ofd = ofd; + io->mainloop = m; + + io->userdata = NULL; + io->callback = NULL; + io->readable = 0; + io->writable = 0; + io->hungup = 0; + io->no_close = 0; + + io->input_event = io->output_event = NULL; + + if (ifd == ofd) { + assert(ifd >= 0); + pa_make_nonblock_fd(io->ifd); + io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io); + } else { + + if (ifd >= 0) { + pa_make_nonblock_fd(io->ifd); + io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io); + } + + if (ofd >= 0) { + pa_make_nonblock_fd(io->ofd); + io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io); + } + } + + return io; +} + +void pa_iochannel_free(pa_iochannel*io) { + assert(io); + + if (io->input_event) + io->mainloop->io_free(io->input_event); + + if (io->output_event && (io->output_event != io->input_event)) + io->mainloop->io_free(io->output_event); + + if (!io->no_close) { + if (io->ifd >= 0) + + close(io->ifd); + if (io->ofd >= 0 && io->ofd != io->ifd) + close(io->ofd); + } + + pa_xfree(io); +} + +int pa_iochannel_is_readable(pa_iochannel*io) { + assert(io); + + return io->readable || io->hungup; +} + +int pa_iochannel_is_writable(pa_iochannel*io) { + assert(io); + + return io->writable && !io->hungup; +} + +int pa_iochannel_is_hungup(pa_iochannel*io) { + assert(io); + + return io->hungup; +} + +ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) { + ssize_t r; + + assert(io); + assert(data); + assert(l); + assert(io->ofd >= 0); + + r = pa_write(io->ofd, data, l); + if (r >= 0) { + io->writable = 0; + enable_mainloop_sources(io); + } + + return r; +} + +ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) { + ssize_t r; + + assert(io); + assert(data); + assert(io->ifd >= 0); + + r = pa_read(io->ifd, data, l); + if (r >= 0) { + io->readable = 0; + enable_mainloop_sources(io); + } + + return r; +} + +#ifdef SCM_CREDENTIALS + +int pa_iochannel_creds_supported(pa_iochannel *io) { + struct sockaddr_un sa; + socklen_t l; + + assert(io); + assert(io->ifd >= 0); + assert(io->ofd == io->ifd); + + l = sizeof(sa); + + if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0) + return 0; + + return sa.sun_family == AF_UNIX; +} + +int pa_iochannel_creds_enable(pa_iochannel *io) { + int t = 1; + + assert(io); + assert(io->ifd >= 0); + + if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) { + pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno)); + return -1; + } + + return 0; +} + +ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l) { + ssize_t r; + struct msghdr mh; + struct iovec iov; + uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))]; + struct ucred *ucred; + struct cmsghdr *cmsg; + + assert(io); + assert(data); + assert(l); + assert(io->ofd >= 0); + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = (void*) data; + iov.iov_len = l; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + cmsg = (struct cmsghdr*) cmsg_data; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + + ucred = (struct ucred*) CMSG_DATA(cmsg); + ucred->pid = getpid(); + ucred->uid = getuid(); + ucred->gid = getgid(); + + memset(&mh, 0, sizeof(mh)); + mh.msg_name = NULL; + mh.msg_namelen = 0; + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = cmsg_data; + mh.msg_controllen = sizeof(cmsg_data); + mh.msg_flags = 0; + + if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) { + io->writable = 0; + enable_mainloop_sources(io); + } + + return r; +} + +ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid) { + ssize_t r; + struct msghdr mh; + struct iovec iov; + uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))]; + + assert(io); + assert(data); + assert(l); + assert(io->ifd >= 0); + assert(ucred); + assert(creds_valid); + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = data; + iov.iov_len = l; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + + memset(&mh, 0, sizeof(mh)); + mh.msg_name = NULL; + mh.msg_namelen = 0; + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = cmsg_data; + mh.msg_controllen = sizeof(cmsg_data); + mh.msg_flags = 0; + + if ((r = recvmsg(io->ifd, &mh, MSG_NOSIGNAL)) >= 0) { + struct cmsghdr *cmsg; + + *creds_valid = 0; + + for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) { + + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { + assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))); + memcpy(ucred, CMSG_DATA(cmsg), sizeof(struct ucred)); + *creds_valid = 1; + break; + } + } + + io->readable = 0; + enable_mainloop_sources(io); + } + + return r; +} +#else /* SCM_CREDENTIALS */ + +int pa_iochannel_creds_supported(pa_iochannel *io) { + return 0; +} + +int pa_iochannel_creds_enable(pa_iochannel *io) { + return -1; +} + +ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l) { + pa_log_error("pa_iochannel_write_with_creds() not supported."); + return -1; +} + +ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid) { + pa_log_error("pa_iochannel_read_with_creds() not supported."); + return -1; +} + +#endif /* SCM_CREDENTIALS */ + +void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) { + assert(io); + + io->callback = _callback; + io->userdata = userdata; +} + +void pa_iochannel_set_noclose(pa_iochannel*io, int b) { + assert(io); + + io->no_close = b; +} + +void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) { + assert(io); + assert(s); + assert(l); + + pa_socket_peer_to_string(io->ifd, s, l); +} + +int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) { + assert(io); + + return pa_socket_set_rcvbuf(io->ifd, l); +} + +int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) { + assert(io); + + return pa_socket_set_sndbuf(io->ofd, l); +} + +pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) { + assert(io); + + return io->mainloop; +} diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h new file mode 100644 index 00000000..64cf331e --- /dev/null +++ b/src/pulsecore/iochannel.h @@ -0,0 +1,81 @@ +#ifndef fooiochannelhfoo +#define fooiochannelhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <sys/types.h> + +#include <pulse/mainloop-api.h> + +/* A wrapper around UNIX file descriptors for attaching them to the a + main event loop. Everytime new data may be read or be written to + the channel a callback function is called. It is safe to destroy + the calling iochannel object from the callback */ + +/* When pa_iochannel_is_readable() returns non-zero, the user has to + * call this function in a loop until it is no longer set or EOF + * reached. Otherwise strange things may happen when an EOF is + * reached. */ + +typedef struct pa_iochannel pa_iochannel; + +/* Create a new IO channel for the specified file descriptors for +input resp. output. It is safe to pass the same file descriptor for +both parameters (in case of full-duplex channels). For a simplex +channel specify -1 for the other direction. */ + +pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd); +void pa_iochannel_free(pa_iochannel*io); + +ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l); +ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l); + +int pa_iochannel_creds_supported(pa_iochannel *io); +int pa_iochannel_creds_enable(pa_iochannel *io); + +struct ucred; + +ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l); +ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid); + +int pa_iochannel_is_readable(pa_iochannel*io); +int pa_iochannel_is_writable(pa_iochannel*io); +int pa_iochannel_is_hungup(pa_iochannel*io); + +/* Don't close the file descirptors when the io channel is freed. By + * default the file descriptors are closed. */ +void pa_iochannel_set_noclose(pa_iochannel*io, int b); + +/* Set the callback function that is called whenever data becomes available for read or write */ +typedef void (*pa_iochannel_cb_t)(pa_iochannel*io, void *userdata); +void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t callback, void *userdata); + +/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */ +void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l); + +/* Use setsockopt() to tune the recieve and send buffers of TCP sockets */ +int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l); +int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l); + +pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io); + +#endif diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c new file mode 100644 index 00000000..9c9900f8 --- /dev/null +++ b/src/pulsecore/ioline.c @@ -0,0 +1,394 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/log.h> + +#include "ioline.h" + +#define BUFFER_LIMIT (64*1024) +#define READ_SIZE (1024) + +struct pa_ioline { + pa_iochannel *io; + pa_defer_event *defer_event; + pa_mainloop_api *mainloop; + int ref; + int dead; + + char *wbuf; + size_t wbuf_length, wbuf_index, wbuf_valid_length; + + char *rbuf; + size_t rbuf_length, rbuf_index, rbuf_valid_length; + + void (*callback)(pa_ioline*io, const char *s, void *userdata); + void *userdata; + + int defer_close; +}; + +static void io_callback(pa_iochannel*io, void *userdata); +static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata); + +pa_ioline* pa_ioline_new(pa_iochannel *io) { + pa_ioline *l; + assert(io); + + l = pa_xnew(pa_ioline, 1); + l->io = io; + l->dead = 0; + + l->wbuf = NULL; + l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0; + + l->rbuf = NULL; + l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0; + + l->callback = NULL; + l->userdata = NULL; + l->ref = 1; + + l->mainloop = pa_iochannel_get_mainloop_api(io); + + l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l); + l->mainloop->defer_enable(l->defer_event, 0); + + l->defer_close = 0; + + pa_iochannel_set_callback(io, io_callback, l); + + return l; +} + +static void ioline_free(pa_ioline *l) { + assert(l); + + if (l->io) + pa_iochannel_free(l->io); + + if (l->defer_event) + l->mainloop->defer_free(l->defer_event); + + pa_xfree(l->wbuf); + pa_xfree(l->rbuf); + pa_xfree(l); +} + +void pa_ioline_unref(pa_ioline *l) { + assert(l); + assert(l->ref >= 1); + + if ((--l->ref) <= 0) + ioline_free(l); +} + +pa_ioline* pa_ioline_ref(pa_ioline *l) { + assert(l); + assert(l->ref >= 1); + + l->ref++; + return l; +} + +void pa_ioline_close(pa_ioline *l) { + assert(l); + assert(l->ref >= 1); + + l->dead = 1; + + if (l->io) { + pa_iochannel_free(l->io); + l->io = NULL; + } + + if (l->defer_event) { + l->mainloop->defer_free(l->defer_event); + l->defer_event = NULL; + } + + if (l->callback) + l->callback = NULL; +} + +void pa_ioline_puts(pa_ioline *l, const char *c) { + size_t len; + + assert(l); + assert(l->ref >= 1); + assert(c); + + if (l->dead) + return; + + len = strlen(c); + if (len > BUFFER_LIMIT - l->wbuf_valid_length) + len = BUFFER_LIMIT - l->wbuf_valid_length; + + if (len) { + assert(l->wbuf_length >= l->wbuf_valid_length); + + /* In case the allocated buffer is too small, enlarge it. */ + if (l->wbuf_valid_length + len > l->wbuf_length) { + size_t n = l->wbuf_valid_length+len; + char *new = pa_xmalloc(n); + if (l->wbuf) { + memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length); + pa_xfree(l->wbuf); + } + l->wbuf = new; + l->wbuf_length = n; + l->wbuf_index = 0; + } else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) { + + /* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */ + memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length); + l->wbuf_index = 0; + } + + assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length); + + /* Append the new string */ + memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len); + l->wbuf_valid_length += len; + + l->mainloop->defer_enable(l->defer_event, 1); + } +} + +void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) { + assert(l); + assert(l->ref >= 1); + + l->callback = callback; + l->userdata = userdata; +} + +static void failure(pa_ioline *l, int process_leftover) { + assert(l); + assert(l->ref >= 1); + assert(!l->dead); + + if (process_leftover && l->rbuf_valid_length > 0) { + /* Pass the last missing bit to the client */ + + if (l->callback) { + char *p = pa_xstrndup(l->rbuf+l->rbuf_index, l->rbuf_valid_length); + l->callback(l, p, l->userdata); + pa_xfree(p); + } + } + + if (l->callback) { + l->callback(l, NULL, l->userdata); + l->callback = NULL; + } + + pa_ioline_close(l); +} + +static void scan_for_lines(pa_ioline *l, size_t skip) { + assert(l && l->ref >= 1 && skip < l->rbuf_valid_length); + + while (!l->dead && l->rbuf_valid_length > skip) { + char *e, *p; + size_t m; + + if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip))) + break; + + *e = 0; + + p = l->rbuf + l->rbuf_index; + m = strlen(p); + + l->rbuf_index += m+1; + l->rbuf_valid_length -= m+1; + + /* A shortcut for the next time */ + if (l->rbuf_valid_length == 0) + l->rbuf_index = 0; + + if (l->callback) + l->callback(l, p, l->userdata); + + skip = 0; + } + + /* If the buffer became too large and still no newline was found, drop it. */ + if (l->rbuf_valid_length >= BUFFER_LIMIT) + l->rbuf_index = l->rbuf_valid_length = 0; +} + +static int do_write(pa_ioline *l); + +static int do_read(pa_ioline *l) { + assert(l && l->ref >= 1); + + while (!l->dead && pa_iochannel_is_readable(l->io)) { + ssize_t r; + size_t len; + + len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; + + /* Check if we have to enlarge the read buffer */ + if (len < READ_SIZE) { + size_t n = l->rbuf_valid_length+READ_SIZE; + + if (n >= BUFFER_LIMIT) + n = BUFFER_LIMIT; + + if (l->rbuf_length >= n) { + /* The current buffer is large enough, let's just move the data to the front */ + if (l->rbuf_valid_length) + memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length); + } else { + /* Enlarge the buffer */ + char *new = pa_xmalloc(n); + if (l->rbuf_valid_length) + memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length); + pa_xfree(l->rbuf); + l->rbuf = new; + l->rbuf_length = n; + } + + l->rbuf_index = 0; + } + + len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; + + assert(len >= READ_SIZE); + + /* Read some data */ + if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) { + if (r < 0) { + pa_log(__FILE__": read(): %s", pa_cstrerror(errno)); + failure(l, 0); + } else + failure(l, 1); + + return -1; + } + + l->rbuf_valid_length += r; + + /* Look if a line has been terminated in the newly read data */ + scan_for_lines(l, l->rbuf_valid_length - r); + } + + return 0; +} + +/* Try to flush the buffer */ +static int do_write(pa_ioline *l) { + ssize_t r; + assert(l && l->ref >= 1); + + while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) { + + if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) { + pa_log(__FILE__": write(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); + failure(l, 0); + return -1; + } + + l->wbuf_index += r; + l->wbuf_valid_length -= r; + + /* A shortcut for the next time */ + if (l->wbuf_valid_length == 0) + l->wbuf_index = 0; + } + + return 0; +} + +/* Try to flush read/write data */ +static void do_work(pa_ioline *l) { + assert(l); + assert(l->ref >= 1); + + pa_ioline_ref(l); + + l->mainloop->defer_enable(l->defer_event, 0); + + if (!l->dead) + do_read(l); + + if (!l->dead) + do_write(l); + + if (l->defer_close && !l->wbuf_valid_length) + failure(l, 1); + + pa_ioline_unref(l); +} + +static void io_callback(pa_iochannel*io, void *userdata) { + pa_ioline *l = userdata; + assert(io && l && l->ref >= 1); + + do_work(l); +} + +static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) { + pa_ioline *l = userdata; + assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e); + + do_work(l); +} + +void pa_ioline_defer_close(pa_ioline *l) { + assert(l); + assert(l->ref >= 1); + + l->defer_close = 1; + + if (!l->wbuf_valid_length) + l->mainloop->defer_enable(l->defer_event, 1); +} + +void pa_ioline_printf(pa_ioline *l, const char *format, ...) { + char *t; + va_list ap; + + assert(l); + assert(l->ref >= 1); + + va_start(ap, format); + t = pa_vsprintf_malloc(format, ap); + va_end(ap); + + pa_ioline_puts(l, t); + pa_xfree(t); +} diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h new file mode 100644 index 00000000..e736e2b3 --- /dev/null +++ b/src/pulsecore/ioline.h @@ -0,0 +1,51 @@ +#ifndef fooiolinehfoo +#define fooiolinehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/iochannel.h> +#include <pulsecore/core-util.h> + +/* An ioline wraps an iochannel for line based communication. A + * callback function is called whenever a new line has been recieved + * from the client */ + +typedef struct pa_ioline pa_ioline; + +pa_ioline* pa_ioline_new(pa_iochannel *io); +void pa_ioline_unref(pa_ioline *l); +pa_ioline* pa_ioline_ref(pa_ioline *l); +void pa_ioline_close(pa_ioline *l); + +/* Write a string to the channel */ +void pa_ioline_puts(pa_ioline *s, const char *c); + +/* Write a string to the channel */ +void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); + +/* Set the callback function that is called for every recieved line */ +void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata); + +/* Make sure to close the ioline object as soon as the send buffer is emptied */ +void pa_ioline_defer_close(pa_ioline *io); + +#endif diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h new file mode 100644 index 00000000..bf3c150a --- /dev/null +++ b/src/pulsecore/llist.h @@ -0,0 +1,79 @@ +#ifndef foollistfoo +#define foollistfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Some macros for maintaining doubly linked lists */ + +/* The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define PA_LLIST_HEAD(t,name) t *name + +/* The pointers in the linked list's items. Use this in the item structure */ +#define PA_LLIST_FIELDS(t) t *next, *prev + +/* Initialize the list's head */ +#define PA_LLIST_HEAD_INIT(t,item) do { (item) = NULL; } while(0) + +/* Initialize a list item */ +#define PA_LLIST_INIT(t,item) do { \ + t *_item = (item); \ + assert(_item); \ + _item->prev = _item->next = NULL; \ + } while(0) + +/* Prepend an item to the list */ +#define PA_LLIST_PREPEND(t,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if ((_item->next = *_head)) \ + _item->next->prev = _item; \ + _item->prev = NULL; \ + *_head = _item; \ + } while (0) + +/* Remove an item from the list */ +#define PA_LLIST_REMOVE(t,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if (_item->next) \ + _item->next->prev = _item->prev; \ + if (_item->prev) \ + _item->prev->next = _item->next; \ + else {\ + assert(*_head == _item); \ + *_head = _item->next; \ + } \ + _item->next = _item->prev = NULL; \ + } while(0) + +#define PA_LLIST_FIND_HEAD(t,item,head) \ +do { \ + t **_head = (head), *_item = (item); \ + *_head = _item; \ + assert(_head); \ + while ((*_head)->prev) \ + *_head = (*_head)->prev; \ +} while (0) \ + + +#endif diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c new file mode 100644 index 00000000..df5b140a --- /dev/null +++ b/src/pulsecore/log.c @@ -0,0 +1,211 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#include <pulse/utf8.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/core-util.h> + +#include "log.h" + +#define ENV_LOGLEVEL "POLYP_LOG" + +static char *log_ident = NULL, *log_ident_local = NULL; +static pa_log_target_t log_target = PA_LOG_STDERR; +static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL; +static pa_log_level_t maximal_level = PA_LOG_NOTICE; + +#ifdef HAVE_SYSLOG_H +static const int level_to_syslog[] = { + [PA_LOG_ERROR] = LOG_ERR, + [PA_LOG_WARN] = LOG_WARNING, + [PA_LOG_NOTICE] = LOG_NOTICE, + [PA_LOG_INFO] = LOG_INFO, + [PA_LOG_DEBUG] = LOG_DEBUG +}; +#endif + +void pa_log_set_ident(const char *p) { + if (log_ident) + pa_xfree(log_ident); + if (log_ident_local) + pa_xfree(log_ident_local); + + log_ident = pa_xstrdup(p); + log_ident_local = pa_utf8_to_locale(log_ident); + if (!log_ident_local) + log_ident_local = pa_xstrdup(log_ident); +} + +void pa_log_set_maximal_level(pa_log_level_t l) { + assert(l < PA_LOG_LEVEL_MAX); + maximal_level = l; +} + +void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) { + assert(t == PA_LOG_USER || !func); + log_target = t; + user_log_func = func; +} + +void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) { + const char *e; + char *text, *t, *n; + + assert(level < PA_LOG_LEVEL_MAX); + + if ((e = getenv(ENV_LOGLEVEL))) + maximal_level = atoi(e); + + if (level > maximal_level) + return; + + text = pa_vsprintf_malloc(format, ap); + + if (!pa_utf8_valid(text)) + pa_log_level(level, __FILE__": invalid UTF-8 string following below:"); + + for (t = text; t; t = n) { + if ((n = strchr(t, '\n'))) { + *n = 0; + n++; + } + + if (!*t) + continue; + + switch (log_target) { + case PA_LOG_STDERR: { + const char *prefix = "", *suffix = ""; + char *local_t; + +#ifndef OS_IS_WIN32 + /* Yes indeed. Useless, but fun! */ + if (isatty(STDERR_FILENO)) { + if (level <= PA_LOG_ERROR) { + prefix = "\x1B[1;31m"; + suffix = "\x1B[0m"; + } else if (level <= PA_LOG_WARN) { + prefix = "\x1B[1m"; + suffix = "\x1B[0m"; + } + } +#endif + + local_t = pa_utf8_to_locale(t); + if (!local_t) + fprintf(stderr, "%s%s%s\n", prefix, t, suffix); + else { + fprintf(stderr, "%s%s%s\n", prefix, local_t, suffix); + pa_xfree(local_t); + } + + break; + } + +#ifdef HAVE_SYSLOG_H + case PA_LOG_SYSLOG: { + char *local_t; + + openlog(log_ident_local ? log_ident_local : "???", LOG_PID, LOG_USER); + + local_t = pa_utf8_to_locale(t); + if (!local_t) + syslog(level_to_syslog[level], "%s", t); + else { + syslog(level_to_syslog[level], "%s", local_t); + pa_xfree(local_t); + } + + closelog(); + break; + } +#endif + + case PA_LOG_USER: + user_log_func(level, t); + break; + + case PA_LOG_NULL: + default: + break; + } + } + + pa_xfree(text); + +} + +void pa_log_level(pa_log_level_t level, const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(level, format, ap); + va_end(ap); +} + +void pa_log_debug(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_DEBUG, format, ap); + va_end(ap); +} + +void pa_log_info(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_INFO, format, ap); + va_end(ap); +} + +void pa_log_notice(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_INFO, format, ap); + va_end(ap); +} + +void pa_log_warn(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_WARN, format, ap); + va_end(ap); +} + +void pa_log_error(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_ERROR, format, ap); + va_end(ap); +} diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h new file mode 100644 index 00000000..7bf4e407 --- /dev/null +++ b/src/pulsecore/log.h @@ -0,0 +1,69 @@ +#ifndef foologhfoo +#define foologhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <stdarg.h> +#include <pulsecore/gccmacro.h> + +/* A simple logging subsystem */ + +/* Where to log to */ +typedef enum pa_log_target { + PA_LOG_STDERR, /* default */ + PA_LOG_SYSLOG, + PA_LOG_USER, /* to user specified function */ + PA_LOG_NULL /* to /dev/null */ +} pa_log_target_t; + +typedef enum pa_log_level { + PA_LOG_ERROR = 0, /* Error messages */ + PA_LOG_WARN = 1, /* Warning messages */ + PA_LOG_NOTICE = 2, /* Notice messages */ + PA_LOG_INFO = 3, /* Info messages */ + PA_LOG_DEBUG = 4, /* debug message */ + PA_LOG_LEVEL_MAX +} pa_log_level_t; + +/* Set an identification for the current daemon. Used when logging to syslog. */ +void pa_log_set_ident(const char *p); + +/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */ +void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s)); + +/* Minimal log level */ +void pa_log_set_maximal_level(pa_log_level_t l); + +/* Do a log line */ +void pa_log_debug(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +void pa_log_info(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +void pa_log_notice(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +void pa_log_warn(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +void pa_log_error(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); + +void pa_log_level(pa_log_level_t level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); + +void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap); + +#define pa_log pa_log_error + +#endif diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c new file mode 100644 index 00000000..8283a7a0 --- /dev/null +++ b/src/pulsecore/mcalign.c @@ -0,0 +1,206 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include "mcalign.h" + +struct pa_mcalign { + size_t base; + pa_memchunk leftover, current; + pa_memblock_stat *memblock_stat; +}; + +pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s) { + pa_mcalign *m; + assert(base); + + m = pa_xnew(pa_mcalign, 1); + + m->base = base; + pa_memchunk_reset(&m->leftover); + pa_memchunk_reset(&m->current); + m->memblock_stat = s; + + return m; +} + +void pa_mcalign_free(pa_mcalign *m) { + assert(m); + + if (m->leftover.memblock) + pa_memblock_unref(m->leftover.memblock); + + if (m->current.memblock) + pa_memblock_unref(m->current.memblock); + + pa_xfree(m); +} + +void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) { + assert(m); + assert(c); + + assert(c->memblock); + assert(c->length > 0); + + assert(!m->current.memblock); + + /* Append to the leftover memory block */ + if (m->leftover.memblock) { + + /* Try to merge */ + if (m->leftover.memblock == c->memblock && + m->leftover.index + m->leftover.length == c->index) { + + /* Merge */ + m->leftover.length += c->length; + + /* If the new chunk is larger than m->base, move it to current */ + if (m->leftover.length >= m->base) { + m->current = m->leftover; + pa_memchunk_reset(&m->leftover); + } + + } else { + size_t l; + + /* We have to copy */ + assert(m->leftover.length < m->base); + l = m->base - m->leftover.length; + + if (l > c->length) + l = c->length; + + /* Can we use the current block? */ + pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base); + + memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l); + m->leftover.length += l; + + assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length); + + if (c->length > l) { + /* Save the remainder of the memory block */ + m->current = *c; + m->current.index += l; + m->current.length -= l; + pa_memblock_ref(m->current.memblock); + } + } + } else { + /* Nothing to merge or copy, just store it */ + + if (c->length >= m->base) + m->current = *c; + else + m->leftover = *c; + + pa_memblock_ref(c->memblock); + } +} + +int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) { + assert(m); + assert(c); + + /* First test if there's a leftover memory block available */ + if (m->leftover.memblock) { + assert(m->leftover.length > 0 && m->leftover.length <= m->base); + + /* The leftover memory block is not yet complete */ + if (m->leftover.length < m->base) + return -1; + + /* Return the leftover memory block */ + *c = m->leftover; + pa_memchunk_reset(&m->leftover); + + /* If the current memblock is too small move it the leftover */ + if (m->current.memblock && m->current.length < m->base) { + m->leftover = m->current; + pa_memchunk_reset(&m->current); + } + + return 0; + } + + /* Now let's see if there is other data available */ + if (m->current.memblock) { + size_t l; + assert(m->current.length >= m->base); + + /* The length of the returned memory block */ + l = m->current.length; + l /= m->base; + l *= m->base; + assert(l > 0); + + /* Prepare the returned block */ + *c = m->current; + pa_memblock_ref(c->memblock); + c->length = l; + + /* Drop that from the current memory block */ + assert(l <= m->current.length); + m->current.index += l; + m->current.length -= l; + + /* In case the whole block was dropped ... */ + if (m->current.length == 0) + pa_memblock_unref(m->current.memblock); + else { + /* Move the raimainder to leftover */ + assert(m->current.length < m->base && !m->leftover.memblock); + + m->leftover = m->current; + } + + pa_memchunk_reset(&m->current); + + return 0; + } + + /* There's simply nothing */ + return -1; + +} + +size_t pa_mcalign_csize(pa_mcalign *m, size_t l) { + assert(m); + assert(l > 0); + + assert(!m->current.memblock); + + if (m->leftover.memblock) + l += m->leftover.length; + + return (l/m->base)*m->base; +} diff --git a/src/pulsecore/mcalign.h b/src/pulsecore/mcalign.h new file mode 100644 index 00000000..80e37499 --- /dev/null +++ b/src/pulsecore/mcalign.h @@ -0,0 +1,80 @@ +#ifndef foomcalignhfoo +#define foomcalignhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/memblock.h> +#include <pulsecore/memchunk.h> + +/* An alignment object, used for aligning memchunks to multiples of + * the frame size. */ + +/* Method of operation: the user creates a new mcalign object by + * calling pa_mcalign_new() with the appropriate aligning + * granularity. After that he may call pa_mcalign_push() for an input + * memchunk. After exactly one memchunk the user has to call + * pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns + * 0, the memchunk *c is valid and aligned to the granularity. Some + * pseudocode illustrating this: + * + * pa_mcalign *a = pa_mcalign_new(4, NULL); + * + * for (;;) { + * pa_memchunk input; + * + * ... fill input ... + * + * pa_mcalign_push(m, &input); + * pa_memblock_unref(input.memblock); + * + * for (;;) { + * pa_memchunk output; + * + * if (pa_mcalign_pop(m, &output) < 0) + * break; + * + * ... consume output ... + * + * pa_memblock_unref(output.memblock); + * } + * } + * + * pa_memchunk_free(a); + * */ + +typedef struct pa_mcalign pa_mcalign; + +pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s); +void pa_mcalign_free(pa_mcalign *m); + +/* Push a new memchunk into the aligner. The caller of this routine + * has to free the memchunk by himself. */ +void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c); + +/* Pop a new memchunk from the aligner. Returns 0 when sucessful, + * nonzero otherwise. */ +int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c); + +/* If we pass l bytes in now, how many bytes would we get out? */ +size_t pa_mcalign_csize(pa_mcalign *m, size_t l); + +#endif diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c new file mode 100644 index 00000000..36de17fb --- /dev/null +++ b/src/pulsecore/memblock.c @@ -0,0 +1,173 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include "memblock.h" + +static void stat_add(pa_memblock*m, pa_memblock_stat *s) { + assert(m); + + if (!s) { + m->stat = NULL; + return; + } + + m->stat = pa_memblock_stat_ref(s); + s->total++; + s->allocated++; + s->total_size += m->length; + s->allocated_size += m->length; +} + +static void stat_remove(pa_memblock *m) { + assert(m); + + if (!m->stat) + return; + + m->stat->total--; + m->stat->total_size -= m->length; + + pa_memblock_stat_unref(m->stat); + m->stat = NULL; +} + +pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s) { + pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)+length); + b->type = PA_MEMBLOCK_APPENDED; + b->ref = 1; + b->length = length; + b->data = b+1; + b->free_cb = NULL; + b->read_only = 0; + stat_add(b, s); + return b; +} + +pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, pa_memblock_stat*s) { + pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)); + b->type = PA_MEMBLOCK_DYNAMIC; + b->ref = 1; + b->length = length; + b->data = d; + b->free_cb = NULL; + b->read_only = 0; + stat_add(b, s); + return b; +} + +pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, pa_memblock_stat*s) { + pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)); + b->type = PA_MEMBLOCK_FIXED; + b->ref = 1; + b->length = length; + b->data = d; + b->free_cb = NULL; + b->read_only = read_only; + stat_add(b, s); + return b; +} + +pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s) { + pa_memblock *b; + assert(d && length && free_cb); + b = pa_xmalloc(sizeof(pa_memblock)); + b->type = PA_MEMBLOCK_USER; + b->ref = 1; + b->length = length; + b->data = d; + b->free_cb = free_cb; + b->read_only = read_only; + stat_add(b, s); + return b; +} + +pa_memblock* pa_memblock_ref(pa_memblock*b) { + assert(b); + assert(b->ref >= 1); + + b->ref++; + return b; +} + +void pa_memblock_unref(pa_memblock*b) { + assert(b); + assert(b->ref >= 1); + + if ((--(b->ref)) == 0) { + stat_remove(b); + + if (b->type == PA_MEMBLOCK_USER) { + assert(b->free_cb); + b->free_cb(b->data); + } else if (b->type == PA_MEMBLOCK_DYNAMIC) + pa_xfree(b->data); + + pa_xfree(b); + } +} + +void pa_memblock_unref_fixed(pa_memblock *b) { + assert(b && b->ref >= 1 && b->type == PA_MEMBLOCK_FIXED); + + if (b->ref == 1) + pa_memblock_unref(b); + else { + b->data = pa_xmemdup(b->data, b->length); + b->type = PA_MEMBLOCK_DYNAMIC; + b->ref--; + } +} + +pa_memblock_stat* pa_memblock_stat_new(void) { + pa_memblock_stat *s; + + s = pa_xmalloc(sizeof(pa_memblock_stat)); + s->ref = 1; + s->total = s->total_size = s->allocated = s->allocated_size = 0; + + return s; +} + +void pa_memblock_stat_unref(pa_memblock_stat *s) { + assert(s && s->ref >= 1); + + if (!(--(s->ref))) { + assert(!s->total); + pa_xfree(s); + } +} + +pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s) { + assert(s); + s->ref++; + return s; +} diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h new file mode 100644 index 00000000..f8545836 --- /dev/null +++ b/src/pulsecore/memblock.h @@ -0,0 +1,86 @@ +#ifndef foomemblockhfoo +#define foomemblockhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <sys/types.h> +#include <inttypes.h> + +/* A pa_memblock is a reference counted memory block. Polypaudio + * passed references to pa_memblocks around instead of copying + * data. See pa_memchunk for a structure that describes parts of + * memory blocks. */ + +/* The type of memory this block points to */ +typedef enum pa_memblock_type { + PA_MEMBLOCK_FIXED, /* data is a pointer to fixed memory that needs not to be freed */ + PA_MEMBLOCK_APPENDED, /* The most common kind: the data is appended to the memory block */ + PA_MEMBLOCK_DYNAMIC, /* data is a pointer to some memory allocated with pa_xmalloc() */ + PA_MEMBLOCK_USER /* User supplied memory, to be freed with free_cb */ +} pa_memblock_type_t; + +/* A structure of keeping memory block statistics */ +/* Maintains statistics about memory blocks */ +typedef struct pa_memblock_stat { + int ref; + unsigned total; + unsigned total_size; + unsigned allocated; + unsigned allocated_size; +} pa_memblock_stat; + +typedef struct pa_memblock { + pa_memblock_type_t type; + unsigned ref; /* the reference counter */ + int read_only; /* boolean */ + size_t length; + void *data; + void (*free_cb)(void *p); /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */ + pa_memblock_stat *stat; +} pa_memblock; + +/* Allocate a new memory block of type PA_MEMBLOCK_APPENDED */ +pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s); + +/* Allocate a new memory block of type PA_MEMBLOCK_DYNAMIC. The pointer data is to be maintained be the memory block */ +pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, pa_memblock_stat*s); + +/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */ +pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, pa_memblock_stat*s); + +/* Allocate a new memory block of type PA_MEMBLOCK_USER */ +pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s); + +void pa_memblock_unref(pa_memblock*b); +pa_memblock* pa_memblock_ref(pa_memblock*b); + +/* This special unref function has to be called by the owner of the +memory of a static memory block when he wants to release all +references to the memory. This causes the memory to be copied and +converted into a PA_MEMBLOCK_DYNAMIC type memory block */ +void pa_memblock_unref_fixed(pa_memblock*b); + +pa_memblock_stat* pa_memblock_stat_new(void); +void pa_memblock_stat_unref(pa_memblock_stat *s); +pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s); + +#endif diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c new file mode 100644 index 00000000..ff199f1b --- /dev/null +++ b/src/pulsecore/memblockq.c @@ -0,0 +1,636 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/time.h> +#include <time.h> +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/log.h> +#include <pulsecore/mcalign.h> + +#include "memblockq.h" + +struct memblock_list { + struct memblock_list *next, *prev; + int64_t index; + pa_memchunk chunk; +}; + +struct pa_memblockq { + struct memblock_list *blocks, *blocks_tail; + unsigned n_blocks; + size_t maxlength, tlength, base, prebuf, minreq; + int64_t read_index, write_index; + enum { PREBUF, RUNNING } state; + pa_memblock_stat *memblock_stat; + pa_memblock *silence; + pa_mcalign *mcalign; +}; + +pa_memblockq* pa_memblockq_new( + int64_t idx, + size_t maxlength, + size_t tlength, + size_t base, + size_t prebuf, + size_t minreq, + pa_memblock *silence, + pa_memblock_stat *s) { + + pa_memblockq* bq; + + assert(base > 0); + assert(maxlength >= base); + + bq = pa_xnew(pa_memblockq, 1); + bq->blocks = bq->blocks_tail = NULL; + bq->n_blocks = 0; + + bq->base = base; + bq->read_index = bq->write_index = idx; + bq->memblock_stat = s; + + pa_log_debug(__FILE__": memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu", + (unsigned long)maxlength, (unsigned long)tlength, (unsigned long)base, (unsigned long)prebuf, (unsigned long)minreq); + + bq->maxlength = ((maxlength+base-1)/base)*base; + assert(bq->maxlength >= base); + + bq->tlength = ((tlength+base-1)/base)*base; + if (!bq->tlength || bq->tlength >= bq->maxlength) + bq->tlength = bq->maxlength; + + bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf; + bq->prebuf = ((bq->prebuf+base-1)/base)*base; + if (bq->prebuf > bq->maxlength) + bq->prebuf = bq->maxlength; + + bq->minreq = (minreq/base)*base; + + if (bq->minreq > bq->tlength - bq->prebuf) + bq->minreq = bq->tlength - bq->prebuf; + + if (!bq->minreq) + bq->minreq = 1; + + pa_log_debug(__FILE__": memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu", + (unsigned long)bq->maxlength, (unsigned long)bq->tlength, (unsigned long)bq->base, (unsigned long)bq->prebuf, (unsigned long)bq->minreq); + + bq->state = bq->prebuf ? PREBUF : RUNNING; + bq->silence = silence ? pa_memblock_ref(silence) : NULL; + bq->mcalign = NULL; + + return bq; +} + +void pa_memblockq_free(pa_memblockq* bq) { + assert(bq); + + pa_memblockq_flush(bq); + + if (bq->silence) + pa_memblock_unref(bq->silence); + + if (bq->mcalign) + pa_mcalign_free(bq->mcalign); + + pa_xfree(bq); +} + +static void drop_block(pa_memblockq *bq, struct memblock_list *q) { + assert(bq); + assert(q); + + assert(bq->n_blocks >= 1); + + if (q->prev) + q->prev->next = q->next; + else + bq->blocks = q->next; + + if (q->next) + q->next->prev = q->prev; + else + bq->blocks_tail = q->prev; + + pa_memblock_unref(q->chunk.memblock); + pa_xfree(q); + + bq->n_blocks--; +} + +static int can_push(pa_memblockq *bq, size_t l) { + int64_t end; + + assert(bq); + + if (bq->read_index > bq->write_index) { + size_t d = bq->read_index - bq->write_index; + + if (l > d) + l -= d; + else + return 1; + } + + end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0; + + /* Make sure that the list doesn't get too long */ + if (bq->write_index + (int64_t)l > end) + if (bq->write_index + l - bq->read_index > bq->maxlength) + return 0; + + return 1; +} + +int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) { + + struct memblock_list *q, *n; + pa_memchunk chunk; + + assert(bq); + assert(uchunk); + assert(uchunk->memblock); + assert(uchunk->length > 0); + assert(uchunk->index + uchunk->length <= uchunk->memblock->length); + + if (uchunk->length % bq->base) + return -1; + + if (!can_push(bq, uchunk->length)) + return -1; + + chunk = *uchunk; + + if (bq->read_index > bq->write_index) { + + /* We currently have a buffer underflow, we need to drop some + * incoming data */ + + size_t d = bq->read_index - bq->write_index; + + if (chunk.length > d) { + chunk.index += d; + chunk.length -= d; + bq->write_index = bq->read_index; + } else { + /* We drop the incoming data completely */ + bq->write_index += chunk.length; + return 0; + } + } + + /* We go from back to front to look for the right place to add + * this new entry. Drop data we will overwrite on the way */ + + q = bq->blocks_tail; + while (q) { + + if (bq->write_index >= q->index + (int64_t)q->chunk.length) + /* We found the entry where we need to place the new entry immediately after */ + break; + else if (bq->write_index + (int64_t)chunk.length <= q->index) { + /* This entry isn't touched at all, let's skip it */ + q = q->prev; + } else if (bq->write_index <= q->index && + bq->write_index + chunk.length >= q->index + q->chunk.length) { + + /* This entry is fully replaced by the new entry, so let's drop it */ + + struct memblock_list *p; + p = q; + q = q->prev; + drop_block(bq, p); + } else if (bq->write_index >= q->index) { + /* The write index points into this memblock, so let's + * truncate or split it */ + + if (bq->write_index + chunk.length < q->index + q->chunk.length) { + + /* We need to save the end of this memchunk */ + struct memblock_list *p; + size_t d; + + /* Create a new list entry for the end of thie memchunk */ + p = pa_xnew(struct memblock_list, 1); + p->chunk = q->chunk; + pa_memblock_ref(p->chunk.memblock); + + /* Calculate offset */ + d = bq->write_index + chunk.length - q->index; + assert(d > 0); + + /* Drop it from the new entry */ + p->index = q->index + d; + p->chunk.length -= d; + + /* Add it to the list */ + p->prev = q; + if ((p->next = q->next)) + q->next->prev = p; + else + bq->blocks_tail = p; + q->next = p; + + bq->n_blocks++; + } + + /* Truncate the chunk */ + if (!(q->chunk.length = bq->write_index - q->index)) { + struct memblock_list *p; + p = q; + q = q->prev; + drop_block(bq, p); + } + + /* We had to truncate this block, hence we're now at the right position */ + break; + } else { + size_t d; + + assert(bq->write_index + (int64_t)chunk.length > q->index && + bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length && + bq->write_index < q->index); + + /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */ + + d = bq->write_index + chunk.length - q->index; + q->index += d; + q->chunk.index += d; + q->chunk.length -= d; + + q = q->prev; + } + + } + + if (q) { + assert(bq->write_index >= q->index + (int64_t)q->chunk.length); + assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index)); + + /* Try to merge memory blocks */ + + if (q->chunk.memblock == chunk.memblock && + q->chunk.index + (int64_t)q->chunk.length == chunk.index && + bq->write_index == q->index + (int64_t)q->chunk.length) { + + q->chunk.length += chunk.length; + bq->write_index += chunk.length; + return 0; + } + } else + assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index)); + + + n = pa_xnew(struct memblock_list, 1); + n->chunk = chunk; + pa_memblock_ref(n->chunk.memblock); + n->index = bq->write_index; + bq->write_index += n->chunk.length; + + n->next = q ? q->next : bq->blocks; + n->prev = q; + + if (n->next) + n->next->prev = n; + else + bq->blocks_tail = n; + + if (n->prev) + n->prev->next = n; + else + bq->blocks = n; + + bq->n_blocks++; + return 0; +} + +int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { + assert(bq); + assert(chunk); + + if (bq->state == PREBUF) { + + /* We need to pre-buffer */ + if (pa_memblockq_get_length(bq) < bq->prebuf) + return -1; + + bq->state = RUNNING; + + } else if (bq->prebuf > 0 && bq->read_index >= bq->write_index) { + + /* Buffer underflow protection */ + bq->state = PREBUF; + return -1; + } + + /* Do we need to spit out silence? */ + if (!bq->blocks || bq->blocks->index > bq->read_index) { + + size_t length; + + /* How much silence shall we return? */ + length = bq->blocks ? bq->blocks->index - bq->read_index : 0; + + /* We need to return silence, since no data is yet available */ + if (bq->silence) { + chunk->memblock = pa_memblock_ref(bq->silence); + + if (!length || length > chunk->memblock->length) + length = chunk->memblock->length; + + chunk->length = length; + } else { + chunk->memblock = NULL; + chunk->length = length; + } + + chunk->index = 0; + return 0; + } + + /* Ok, let's pass real data to the caller */ + assert(bq->blocks->index == bq->read_index); + + *chunk = bq->blocks->chunk; + pa_memblock_ref(chunk->memblock); + + return 0; +} + +void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) { + assert(bq); + assert(length % bq->base == 0); + + assert(!chunk || length <= chunk->length); + + if (chunk) { + + if (bq->blocks && bq->blocks->index == bq->read_index) { + /* The first item in queue is valid */ + + /* Does the chunk match with what the user supplied us? */ + if (memcmp(chunk, &bq->blocks->chunk, sizeof(pa_memchunk)) != 0) + return; + + } else { + size_t l; + + /* The first item in the queue is not yet relevant */ + + assert(!bq->blocks || bq->blocks->index > bq->read_index); + l = bq->blocks ? bq->blocks->index - bq->read_index : 0; + + if (bq->silence) { + + if (!l || l > bq->silence->length) + l = bq->silence->length; + + } + + /* Do the entries still match? */ + if (chunk->index != 0 || chunk->length != l || chunk->memblock != bq->silence) + return; + } + } + + while (length > 0) { + + if (bq->blocks) { + size_t d; + + assert(bq->blocks->index >= bq->read_index); + + d = (size_t) (bq->blocks->index - bq->read_index); + + if (d >= length) { + /* The first block is too far in the future */ + + bq->read_index += length; + break; + } else { + + length -= d; + bq->read_index += d; + } + + assert(bq->blocks->index == bq->read_index); + + if (bq->blocks->chunk.length <= length) { + /* We need to drop the full block */ + + length -= bq->blocks->chunk.length; + bq->read_index += bq->blocks->chunk.length; + + drop_block(bq, bq->blocks); + } else { + /* Only the start of this block needs to be dropped */ + + bq->blocks->chunk.index += length; + bq->blocks->chunk.length -= length; + bq->blocks->index += length; + bq->read_index += length; + break; + } + + } else { + + /* The list is empty, there's nothing we could drop */ + bq->read_index += length; + break; + } + } +} + +int pa_memblockq_is_readable(pa_memblockq *bq) { + assert(bq); + + if (bq->prebuf > 0) { + size_t l = pa_memblockq_get_length(bq); + + if (bq->state == PREBUF && l < bq->prebuf) + return 0; + + if (l <= 0) + return 0; + } + + return 1; +} + +int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) { + assert(bq); + + if (length % bq->base) + return 0; + + return pa_memblockq_get_length(bq) + length <= bq->tlength; +} + +size_t pa_memblockq_get_length(pa_memblockq *bq) { + assert(bq); + + if (bq->write_index <= bq->read_index) + return 0; + + return (size_t) (bq->write_index - bq->read_index); +} + +size_t pa_memblockq_missing(pa_memblockq *bq) { + size_t l; + assert(bq); + + if ((l = pa_memblockq_get_length(bq)) >= bq->tlength) + return 0; + + l = bq->tlength - l; + return (l >= bq->minreq) ? l : 0; +} + +size_t pa_memblockq_get_minreq(pa_memblockq *bq) { + assert(bq); + + return bq->minreq; +} + +void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) { + assert(bq); + + switch (seek) { + case PA_SEEK_RELATIVE: + bq->write_index += offset; + return; + case PA_SEEK_ABSOLUTE: + bq->write_index = offset; + return; + case PA_SEEK_RELATIVE_ON_READ: + bq->write_index = bq->read_index + offset; + return; + case PA_SEEK_RELATIVE_END: + bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t)bq->blocks_tail->chunk.length : bq->read_index) + offset; + return; + } + + assert(0); +} + +void pa_memblockq_flush(pa_memblockq *bq) { + assert(bq); + + while (bq->blocks) + drop_block(bq, bq->blocks); + + assert(bq->n_blocks == 0); + + bq->write_index = bq->read_index; + + pa_memblockq_prebuf_force(bq); +} + +size_t pa_memblockq_get_tlength(pa_memblockq *bq) { + assert(bq); + + return bq->tlength; +} + +int64_t pa_memblockq_get_read_index(pa_memblockq *bq) { + assert(bq); + return bq->read_index; +} + +int64_t pa_memblockq_get_write_index(pa_memblockq *bq) { + assert(bq); + return bq->write_index; +} + +int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) { + pa_memchunk rchunk; + + assert(bq); + assert(chunk && bq->base); + + if (bq->base == 1) + return pa_memblockq_push(bq, chunk); + + if (!bq->mcalign) + bq->mcalign = pa_mcalign_new(bq->base, bq->memblock_stat); + + if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length))) + return -1; + + pa_mcalign_push(bq->mcalign, chunk); + + while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) { + int r; + r = pa_memblockq_push(bq, &rchunk); + pa_memblock_unref(rchunk.memblock); + + if (r < 0) + return -1; + } + + return 0; +} + +void pa_memblockq_shorten(pa_memblockq *bq, size_t length) { + size_t l; + assert(bq); + + l = pa_memblockq_get_length(bq); + + if (l > length) + pa_memblockq_drop(bq, NULL, l - length); +} + +void pa_memblockq_prebuf_disable(pa_memblockq *bq) { + assert(bq); + + if (bq->state == PREBUF) + bq->state = RUNNING; +} + +void pa_memblockq_prebuf_force(pa_memblockq *bq) { + assert(bq); + + if (bq->state == RUNNING && bq->prebuf > 0) + bq->state = PREBUF; +} + +size_t pa_memblockq_get_maxlength(pa_memblockq *bq) { + assert(bq); + + return bq->maxlength; +} + +size_t pa_memblockq_get_prebuf(pa_memblockq *bq) { + assert(bq); + + return bq->prebuf; +} diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h new file mode 100644 index 00000000..c35b62dd --- /dev/null +++ b/src/pulsecore/memblockq.h @@ -0,0 +1,140 @@ +#ifndef foomemblockqhfoo +#define foomemblockqhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <sys/types.h> +#include <inttypes.h> + +#include <pulsecore/memblock.h> +#include <pulsecore/memchunk.h> +#include <pulse/def.h> + +/* A memblockq is a queue of pa_memchunks (yepp, the name is not + * perfect). It is similar to the ring buffers used by most other + * audio software. In contrast to a ring buffer this memblockq data + * type doesn't need to copy any data around, it just maintains + * references to reference counted memory blocks. */ + +typedef struct pa_memblockq pa_memblockq; + + +/* Parameters: + + - idx: start value for both read and write index + + - maxlength: maximum length of queue. If more data is pushed into + the queue, the operation will fail. Must not be 0. + + - tlength: the target length of the queue. Pass 0 for the default. + + - base: a base value for all metrics. Only multiples of this value + are popped from the queue or should be pushed into + it. Must not be 0. + + - prebuf: If the queue runs empty wait until this many bytes are in + queue again before passing the first byte out. If set + to 0 pa_memblockq_pop() will return a silence memblock + if no data is in the queue and will never fail. Pass + (size_t) -1 for the default. + + - minreq: pa_memblockq_missing() will only return values greater + than this value. Pass 0 for the default. + + - silence: return this memblock whzen reading unitialized data +*/ +pa_memblockq* pa_memblockq_new( + int64_t idx, + size_t maxlength, + size_t tlength, + size_t base, + size_t prebuf, + size_t minreq, + pa_memblock *silence, + pa_memblock_stat *s); + +void pa_memblockq_free(pa_memblockq*bq); + +/* Push a new memory chunk into the queue. */ +int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk); + +/* Push a new memory chunk into the queue, but filter it through a + * pa_mcalign object. Don't mix this with pa_memblockq_seek() unless + * you know what you do. */ +int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk); + +/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */ +int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk); + +/* Drop the specified bytes from the queue, but only if the first + * chunk in the queue matches the one passed here. If NULL is passed, + * this check isn't done. */ +void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length); + +/* Test if the pa_memblockq is currently readable, that is, more data than base */ +int pa_memblockq_is_readable(pa_memblockq *bq); + +/* Test if the pa_memblockq is currently writable for the specified amount of bytes */ +int pa_memblockq_is_writable(pa_memblockq *bq, size_t length); + +/* Return the length of the queue in bytes */ +size_t pa_memblockq_get_length(pa_memblockq *bq); + +/* Return how many bytes are missing in queue to the specified fill amount */ +size_t pa_memblockq_missing(pa_memblockq *bq); + +/* Returns the minimal request value */ +size_t pa_memblockq_get_minreq(pa_memblockq *bq); + +/* Manipulate the write pointer */ +void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek); + +/* Set the queue to silence, set write index to read index */ +void pa_memblockq_flush(pa_memblockq *bq); + +/* Get Target length */ +size_t pa_memblockq_get_tlength(pa_memblockq *bq); + +/* Return the current read index */ +int64_t pa_memblockq_get_read_index(pa_memblockq *bq); + +/* Return the current write index */ +int64_t pa_memblockq_get_write_index(pa_memblockq *bq); + +/* Shorten the pa_memblockq to the specified length by dropping data + * at the read end of the queue. The read index is increased until the + * queue has the specified length */ +void pa_memblockq_shorten(pa_memblockq *bq, size_t length); + +/* Ignore prebuf for now */ +void pa_memblockq_prebuf_disable(pa_memblockq *bq); + +/* Force prebuf */ +void pa_memblockq_prebuf_force(pa_memblockq *bq); + +/* Return the maximum length of the queue in bytes */ +size_t pa_memblockq_get_maxlength(pa_memblockq *bq); + +/* Return the prebuffer length in bytes */ +size_t pa_memblockq_get_prebuf(pa_memblockq *bq); + +#endif diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c new file mode 100644 index 00000000..abfc2cab --- /dev/null +++ b/src/pulsecore/memchunk.c @@ -0,0 +1,59 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include "memchunk.h" + +void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min) { + pa_memblock *n; + size_t l; + assert(c && c->memblock && c->memblock->ref >= 1); + + if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min) + return; + + l = c->length; + if (l < min) + l = min; + + n = pa_memblock_new(l, s); + memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length); + pa_memblock_unref(c->memblock); + c->memblock = n; + c->index = 0; +} + +void pa_memchunk_reset(pa_memchunk *c) { + assert(c); + + c->memblock = NULL; + c->length = c->index = 0; +} diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h new file mode 100644 index 00000000..1b26c2e6 --- /dev/null +++ b/src/pulsecore/memchunk.h @@ -0,0 +1,45 @@ +#ifndef foomemchunkhfoo +#define foomemchunkhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/memblock.h> + +/* A memchunk describes a part of a memblock. In contrast to the memblock, a + * memchunk is not allocated dynamically or reference counted, instead + * it is usually stored on the stack and copied around */ + +typedef struct pa_memchunk { + pa_memblock *memblock; + size_t index, length; +} pa_memchunk; + +/* Make a memchunk writable, i.e. make sure that the caller may have + * exclusive access to the memblock and it is not read_only. If needed + * the memblock in the structure is replaced by a copy. */ +void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min); + +/* Invalidate a memchunk. This does not free the cotaining memblock, + * but sets all members to zero. */ +void pa_memchunk_reset(pa_memchunk *c); + +#endif diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c new file mode 100644 index 00000000..13a48785 --- /dev/null +++ b/src/pulsecore/modargs.c @@ -0,0 +1,310 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <ctype.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/hashmap.h> +#include <pulsecore/idxset.h> +#include <pulsecore/sample-util.h> +#include <pulsecore/namereg.h> +#include <pulsecore/sink.h> +#include <pulsecore/source.h> +#include <pulsecore/core-util.h> + +#include "modargs.h" + +struct entry { + char *key, *value; +}; + +static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) { + struct entry *e; + assert(map && key && value); + + if (valid_keys) { + const char*const* v; + for (v = valid_keys; *v; v++) + if (strcmp(*v, key) == 0) + break; + + if (!*v) { + pa_xfree(key); + pa_xfree(value); + return -1; + } + } + + e = pa_xmalloc(sizeof(struct entry)); + e->key = key; + e->value = value; + pa_hashmap_put(map, key, e); + return 0; +} + +pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { + pa_hashmap *map = NULL; + + map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(map); + + if (args) { + enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state; + const char *p, *key, *value; + size_t key_len = 0, value_len = 0; + + key = value = NULL; + state = WHITESPACE; + for (p = args; *p; p++) { + switch (state) { + case WHITESPACE: + if (*p == '=') + goto fail; + else if (!isspace(*p)) { + key = p; + state = KEY; + key_len = 1; + } + break; + case KEY: + if (*p == '=') + state = VALUE_START; + else + key_len++; + break; + case VALUE_START: + if (*p == '\'') { + state = VALUE_TICKS; + value = p+1; + value_len = 0; + } else if (*p == '"') { + state = VALUE_DOUBLE_QUOTES; + value = p+1; + value_len = 0; + } else if (isspace(*p)) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else { + state = VALUE_SIMPLE; + value = p; + value_len = 1; + } + break; + case VALUE_SIMPLE: + if (isspace(*p)) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + case VALUE_DOUBLE_QUOTES: + if (*p == '"') { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + case VALUE_TICKS: + if (*p == '\'') { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + } + } + + if (state == VALUE_START) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) + goto fail; + } else if (state == VALUE_SIMPLE) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0) + goto fail; + } else if (state != WHITESPACE) + goto fail; + } + + return (pa_modargs*) map; + +fail: + + if (map) + pa_modargs_free((pa_modargs*) map); + + return NULL; +} + + +static void free_func(void *p, PA_GCC_UNUSED void*userdata) { + struct entry *e = p; + assert(e); + pa_xfree(e->key); + pa_xfree(e->value); + pa_xfree(e); +} + +void pa_modargs_free(pa_modargs*ma) { + pa_hashmap *map = (pa_hashmap*) ma; + pa_hashmap_free(map, free_func, NULL); +} + +const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def) { + pa_hashmap *map = (pa_hashmap*) ma; + struct entry*e; + + if (!(e = pa_hashmap_get(map, key))) + return def; + + return e->value; +} + +int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) { + const char *v; + assert(ma && key && value); + + if (!(v = pa_modargs_get_value(ma, key, NULL))) + return 0; + + if (pa_atou(v, value) < 0) + return -1; + + return 0; +} + +int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) { + const char *v; + assert(ma && key && value); + + if (!(v = pa_modargs_get_value(ma, key, NULL))) + return 0; + + if (pa_atoi(v, value) < 0) + return -1; + + return 0; +} + +int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) { + const char *v; + int r; + assert(ma && key && value); + + if (!(v = pa_modargs_get_value(ma, key, NULL))) + return 0; + + if (!*v) + return -1; + + if ((r = pa_parse_boolean(v)) < 0) + return -1; + + *value = r; + return 0; +} + +int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) { + const char *format; + uint32_t channels; + pa_sample_spec ss; + assert(ma && rss); + +/* DEBUG_TRAP;*/ + + ss = *rss; + if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0) + return -1; + + channels = ss.channels; + if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0) + return -1; + ss.channels = (uint8_t) channels; + + if ((format = pa_modargs_get_value(ma, "format", NULL))) + if ((ss.format = pa_parse_sample_format(format)) < 0) + return -1; + + if (!pa_sample_spec_valid(&ss)) + return -1; + + *rss = ss; + + return 0; +} + +int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *rmap) { + pa_channel_map map; + const char *cm; + + assert(ma); + assert(rmap); + + map = *rmap; + + if ((cm = pa_modargs_get_value(ma, "channel_map", NULL))) + if (!pa_channel_map_parse(&map, cm)) + return -1; + + if (!pa_channel_map_valid(&map)) + return -1; + + *rmap = map; + return 0; +} + +int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *rss, pa_channel_map *rmap, pa_channel_map_def_t def) { + pa_sample_spec ss; + pa_channel_map map; + + assert(ma); + assert(rss); + assert(rmap); + + ss = *rss; + + if (pa_modargs_get_sample_spec(ma, &ss) < 0) + return -1; + + if (!pa_channel_map_init_auto(&map, ss.channels, def)) + map.channels = 0; + + if (pa_modargs_get_channel_map(ma, &map) < 0) + return -1; + + if (map.channels != ss.channels) + return -1; + + *rmap = map; + *rss = ss; + + return 0; +} diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h new file mode 100644 index 00000000..730cf396 --- /dev/null +++ b/src/pulsecore/modargs.h @@ -0,0 +1,60 @@ +#ifndef foomodargshfoo +#define foomodargshfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> +#include <pulse/sample.h> +#include <pulse/channelmap.h> +#include <pulsecore/core.h> + +typedef struct pa_modargs pa_modargs; + +/* A generic parser for module arguments */ + +/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */ +pa_modargs *pa_modargs_new(const char *args, const char* const keys[]); +void pa_modargs_free(pa_modargs*ma); + +/* Return the module argument for the specified name as a string. If + * the argument was not specified, return def instead.*/ +const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def); + +/* Return a module argument as unsigned 32bit value in *value */ +int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value); +int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value); +int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value); + +/* Return sample spec data from the three arguments "rate", "format" and "channels" */ +int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss); + +/* Return channel map data from the argument "channel_map" */ +int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *map); + +/* Combination of pa_modargs_get_sample_spec() and +pa_modargs_get_channel_map(). Not always suitable, since this routine +initializes the map parameter based on the channels field of the ss +structure if no channel_map is found, using pa_channel_map_init_auto() */ + +int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *ss, pa_channel_map *map, pa_channel_map_def_t def); + +#endif diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c new file mode 100644 index 00000000..adefdb46 --- /dev/null +++ b/src/pulsecore/modinfo.c @@ -0,0 +1,93 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <ltdl.h> +#include <assert.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-util.h> +#include <pulsecore/log.h> + +#include "modinfo.h" + +#define PA_SYMBOL_AUTHOR "pa__get_author" +#define PA_SYMBOL_DESCRIPTION "pa__get_description" +#define PA_SYMBOL_USAGE "pa__get_usage" +#define PA_SYMBOL_VERSION "pa__get_version" + +/* lt_dlsym() violates ISO C, so confide the breakage into this function to + * avoid warnings. */ +typedef void (*fnptr)(void); +static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) { + return (fnptr) (long) lt_dlsym(handle, symbol); +} + +pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) { + pa_modinfo *i; + const char* (*func)(void); + assert(dl); + + i = pa_xnew0(pa_modinfo, 1); + + if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_AUTHOR))) + i->author = pa_xstrdup(func()); + + if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_DESCRIPTION))) + i->description = pa_xstrdup(func()); + + if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_USAGE))) + i->usage = pa_xstrdup(func()); + + if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_VERSION))) + i->version = pa_xstrdup(func()); + + return i; +} + +pa_modinfo *pa_modinfo_get_by_name(const char *name) { + lt_dlhandle dl; + pa_modinfo *i; + assert(name); + + if (!(dl = lt_dlopenext(name))) { + pa_log(__FILE__": Failed to open module \"%s\": %s", name, lt_dlerror()); + return NULL; + } + + i = pa_modinfo_get_by_handle(dl); + lt_dlclose(dl); + + return i; +} + +void pa_modinfo_free(pa_modinfo *i) { + assert(i); + pa_xfree(i->author); + pa_xfree(i->description); + pa_xfree(i->usage); + pa_xfree(i->version); + pa_xfree(i); +} diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h new file mode 100644 index 00000000..f39cee3b --- /dev/null +++ b/src/pulsecore/modinfo.h @@ -0,0 +1,43 @@ +#ifndef foomodinfohfoo +#define foomodinfohfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Some functions for reading module meta data from Polypaudio modules */ + +typedef struct pa_modinfo { + char *author; + char *description; + char *usage; + char *version; +} pa_modinfo; + +/* Read meta data from an libtool handle */ +pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl); + +/* Read meta data from a module file */ +pa_modinfo *pa_modinfo_get_by_name(const char *name); + +/* Free meta data */ +void pa_modinfo_free(pa_modinfo *i); + +#endif diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c new file mode 100644 index 00000000..e7dca78d --- /dev/null +++ b/src/pulsecore/module.c @@ -0,0 +1,319 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include <pulse/timeval.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/core-subscribe.h> +#include <pulsecore/log.h> +#include <pulsecore/core-util.h> + +#include "module.h" + +#define PA_SYMBOL_INIT "pa__init" +#define PA_SYMBOL_DONE "pa__done" + +#define UNLOAD_POLL_TIME 2 + +/* lt_dlsym() violates ISO C, so confide the breakage into this function to + * avoid warnings. */ +typedef void (*fnptr)(void); +static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) { + return (fnptr) (long) lt_dlsym(handle, symbol); +} + +static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + pa_core *c = userdata; + struct timeval ntv; + assert(c && c->mainloop == m && c->module_auto_unload_event == e); + + pa_module_unload_unused(c); + + pa_gettimeofday(&ntv); + ntv.tv_sec += UNLOAD_POLL_TIME; + m->time_restart(e, &ntv); +} + +static inline fnptr load_sym(lt_dlhandle handle, const char *module, const char *symbol) { + char *buffer, *ch; + size_t buflen; + fnptr res; + + res = lt_dlsym_fn(handle, symbol); + if (res) + return res; + + /* As the .la files might have been cleansed from the system, we should + * try with the ltdl prefix as well. */ + + buflen = strlen(symbol) + strlen(module) + strlen("_LTX_") + 1; + buffer = pa_xmalloc(buflen); + assert(buffer); + + strcpy(buffer, module); + + for (ch = buffer;*ch != '\0';ch++) { + if (!isalnum(*ch)) + *ch = '_'; + } + + strcat(buffer, "_LTX_"); + strcat(buffer, symbol); + + res = lt_dlsym_fn(handle, buffer); + + pa_xfree(buffer); + + return res; +} + +pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { + pa_module *m = NULL; + int r; + + assert(c && name); + + if (c->disallow_module_loading) + goto fail; + + m = pa_xmalloc(sizeof(pa_module)); + + m->name = pa_xstrdup(name); + m->argument = pa_xstrdup(argument); + + if (!(m->dl = lt_dlopenext(name))) { + pa_log(__FILE__": Failed to open module \"%s\": %s", name, lt_dlerror()); + goto fail; + } + + if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_INIT))) { + pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name); + goto fail; + } + + if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_DONE))) { + pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.", name); + goto fail; + } + + m->userdata = NULL; + m->core = c; + m->n_used = -1; + m->auto_unload = 0; + m->unload_requested = 0; + + assert(m->init); + if (m->init(c, m) < 0) { + pa_log_error(__FILE__": Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : ""); + goto fail; + } + + if (!c->modules) + c->modules = pa_idxset_new(NULL, NULL); + + if (!c->module_auto_unload_event) { + struct timeval ntv; + pa_gettimeofday(&ntv); + ntv.tv_sec += UNLOAD_POLL_TIME; + c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c); + } + assert(c->module_auto_unload_event); + + assert(c->modules); + r = pa_idxset_put(c->modules, m, &m->index); + assert(r >= 0 && m->index != PA_IDXSET_INVALID); + + pa_log_info(__FILE__": Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : ""); + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index); + + return m; + +fail: + + if (m) { + pa_xfree(m->argument); + pa_xfree(m->name); + + if (m->dl) + lt_dlclose(m->dl); + + pa_xfree(m); + } + + return NULL; +} + +static void pa_module_free(pa_module *m) { + assert(m && m->done && m->core); + + if (m->core->disallow_module_loading) + return; + + pa_log_info(__FILE__": Unloading \"%s\" (index: #%u).", m->name, m->index); + + m->done(m->core, m); + + lt_dlclose(m->dl); + + pa_log_info(__FILE__": Unloaded \"%s\" (index: #%u).", m->name, m->index); + + pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index); + + pa_xfree(m->name); + pa_xfree(m->argument); + pa_xfree(m); +} + +void pa_module_unload(pa_core *c, pa_module *m) { + assert(c && m); + + assert(c->modules); + if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL))) + return; + + pa_module_free(m); +} + +void pa_module_unload_by_index(pa_core *c, uint32_t idx) { + pa_module *m; + assert(c && idx != PA_IDXSET_INVALID); + + assert(c->modules); + if (!(m = pa_idxset_remove_by_index(c->modules, idx))) + return; + + pa_module_free(m); +} + +static void free_callback(void *p, PA_GCC_UNUSED void *userdata) { + pa_module *m = p; + assert(m); + pa_module_free(m); +} + +void pa_module_unload_all(pa_core *c) { + assert(c); + + if (!c->modules) + return; + + pa_idxset_free(c->modules, free_callback, NULL); + c->modules = NULL; + + if (c->module_auto_unload_event) { + c->mainloop->time_free(c->module_auto_unload_event); + c->module_auto_unload_event = NULL; + } + + if (c->module_defer_unload_event) { + c->mainloop->defer_free(c->module_defer_unload_event); + c->module_defer_unload_event = NULL; + } +} + +static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) { + pa_module *m = p; + time_t *now = userdata; + assert(p && del && now); + + if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) { + pa_module_free(m); + *del = 1; + } + + return 0; +} + +void pa_module_unload_unused(pa_core *c) { + time_t now; + assert(c); + + if (!c->modules) + return; + + time(&now); + pa_idxset_foreach(c->modules, unused_callback, &now); +} + +static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) { + pa_module *m = p; + assert(m); + + if (m->unload_requested) { + pa_module_free(m); + *del = 1; + } + + return 0; +} + +static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) { + pa_core *core = userdata; + api->defer_enable(e, 0); + + if (!core->modules) + return; + + pa_idxset_foreach(core->modules, unload_callback, NULL); + +} + +void pa_module_unload_request(pa_module *m) { + assert(m); + + m->unload_requested = 1; + + if (!m->core->module_defer_unload_event) + m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core); + + m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1); +} + +void pa_module_set_used(pa_module*m, int used) { + assert(m); + + if (m->n_used != used) + pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index); + + if (m->n_used != used && used == 0) + time(&m->last_used_time); + + m->n_used = used; +} + +pa_modinfo *pa_module_get_info(pa_module *m) { + assert(m); + + return pa_modinfo_get_by_handle(m->dl); +} diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h new file mode 100644 index 00000000..9bc5cb5d --- /dev/null +++ b/src/pulsecore/module.h @@ -0,0 +1,70 @@ +#ifndef foomodulehfoo +#define foomodulehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> +#include <ltdl.h> + +#include <pulsecore/core.h> +#include <pulsecore/modinfo.h> + +typedef struct pa_module pa_module; + +struct pa_module { + pa_core *core; + char *name, *argument; + uint32_t index; + + lt_dlhandle dl; + + int (*init)(pa_core *c, pa_module*m); + void (*done)(pa_core *c, pa_module*m); + + void *userdata; + + int n_used; + int auto_unload; + time_t last_used_time; + + int unload_requested; +}; + +pa_module* pa_module_load(pa_core *c, const char *name, const char*argument); +void pa_module_unload(pa_core *c, pa_module *m); +void pa_module_unload_by_index(pa_core *c, uint32_t idx); + +void pa_module_unload_all(pa_core *c); +void pa_module_unload_unused(pa_core *c); + +void pa_module_unload_request(pa_module *m); + +void pa_module_set_used(pa_module*m, int used); + +#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; } +#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; } +#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; } +#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; } + +pa_modinfo *pa_module_get_info(pa_module *m); + +#endif diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c new file mode 100644 index 00000000..0f35ed1c --- /dev/null +++ b/src/pulsecore/namereg.c @@ -0,0 +1,214 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/autoload.h> +#include <pulsecore/source.h> +#include <pulsecore/sink.h> +#include <pulsecore/core-subscribe.h> +#include <pulsecore/core-util.h> + +#include "namereg.h" + +struct namereg_entry { + pa_namereg_type_t type; + char *name; + void *data; +}; + +void pa_namereg_free(pa_core *c) { + assert(c); + if (!c->namereg) + return; + assert(pa_hashmap_size(c->namereg) == 0); + pa_hashmap_free(c->namereg, NULL, NULL); +} + +const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) { + struct namereg_entry *e; + char *n = NULL; + int r; + + assert(c && name && data); + + if (!c->namereg) { + c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(c->namereg); + } + + if ((e = pa_hashmap_get(c->namereg, name)) && fail) + return NULL; + + if (!e) + n = pa_xstrdup(name); + else { + unsigned i; + size_t l = strlen(name); + n = pa_xmalloc(l+3); + + for (i = 1; i <= 99; i++) { + snprintf(n, l+2, "%s%u", name, i); + + if (!(e = pa_hashmap_get(c->namereg, n))) + break; + } + + if (e) { + pa_xfree(n); + return NULL; + } + } + + assert(n); + e = pa_xmalloc(sizeof(struct namereg_entry)); + e->type = type; + e->name = n; + e->data = data; + + r = pa_hashmap_put(c->namereg, e->name, e); + assert (r >= 0); + + return e->name; + +} + +void pa_namereg_unregister(pa_core *c, const char *name) { + struct namereg_entry *e; + assert(c && name); + + e = pa_hashmap_remove(c->namereg, name); + assert(e); + + pa_xfree(e->name); + pa_xfree(e); +} + +void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) { + struct namereg_entry *e; + uint32_t idx; + assert(c); + + if (!name) { + if (type == PA_NAMEREG_SOURCE) + name = pa_namereg_get_default_source_name(c); + else if (type == PA_NAMEREG_SINK) + name = pa_namereg_get_default_sink_name(c); + } + + if (!name) + return NULL; + + if (c->namereg && (e = pa_hashmap_get(c->namereg, name))) + if (e->type == type) + return e->data; + + if (pa_atou(name, &idx) < 0) { + + if (autoload) { + pa_autoload_request(c, name, type); + + if (c->namereg && (e = pa_hashmap_get(c->namereg, name))) + if (e->type == type) + return e->data; + } + + return NULL; + } + + if (type == PA_NAMEREG_SINK) + return pa_idxset_get_by_index(c->sinks, idx); + else if (type == PA_NAMEREG_SOURCE) + return pa_idxset_get_by_index(c->sources, idx); + else if (type == PA_NAMEREG_SAMPLE && c->scache) + return pa_idxset_get_by_index(c->scache, idx); + + return NULL; +} + +void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) { + char **s; + assert(c && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE)); + + s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name; + assert(s); + + if (!name && !*s) + return; + + if (name && *s && !strcmp(name, *s)) + return; + + pa_xfree(*s); + *s = pa_xstrdup(name); + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); +} + +const char *pa_namereg_get_default_sink_name(pa_core *c) { + pa_sink *s; + assert(c); + + if (c->default_sink_name) + return c->default_sink_name; + + if ((s = pa_idxset_first(c->sinks, NULL))) + pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK); + + if (c->default_sink_name) + return c->default_sink_name; + + return NULL; +} + +const char *pa_namereg_get_default_source_name(pa_core *c) { + pa_source *s; + uint32_t idx; + + assert(c); + + if (c->default_source_name) + return c->default_source_name; + + for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx)) + if (!s->monitor_of) { + pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE); + break; + } + + if (!c->default_source_name) + if ((s = pa_idxset_first(c->sources, NULL))) + pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE); + + if (c->default_source_name) + return c->default_source_name; + + return NULL; +} diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h new file mode 100644 index 00000000..e1aef8bb --- /dev/null +++ b/src/pulsecore/namereg.h @@ -0,0 +1,43 @@ +#ifndef foonamereghfoo +#define foonamereghfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/core.h> + +typedef enum pa_namereg_type { + PA_NAMEREG_SINK, + PA_NAMEREG_SOURCE, + PA_NAMEREG_SAMPLE +} pa_namereg_type_t; + +void pa_namereg_free(pa_core *c); + +const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail); +void pa_namereg_unregister(pa_core *c, const char *name); +void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload); +void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type); + +const char *pa_namereg_get_default_sink_name(pa_core *c); +const char *pa_namereg_get_default_source_name(pa_core *c); + +#endif diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h new file mode 100644 index 00000000..b35931d0 --- /dev/null +++ b/src/pulsecore/native-common.h @@ -0,0 +1,127 @@ +#ifndef foonativecommonhfoo +#define foonativecommonhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulse/cdecl.h> +#include <pulse/def.h> + +PA_C_DECL_BEGIN + +enum { + /* Generic commands */ + PA_COMMAND_ERROR, + PA_COMMAND_TIMEOUT, /* pseudo command */ + PA_COMMAND_REPLY, + + /* Commands from client to server */ + PA_COMMAND_CREATE_PLAYBACK_STREAM, + PA_COMMAND_DELETE_PLAYBACK_STREAM, + PA_COMMAND_CREATE_RECORD_STREAM, + PA_COMMAND_DELETE_RECORD_STREAM, + PA_COMMAND_EXIT, + PA_COMMAND_AUTH, + PA_COMMAND_SET_CLIENT_NAME, + PA_COMMAND_LOOKUP_SINK, + PA_COMMAND_LOOKUP_SOURCE, + PA_COMMAND_DRAIN_PLAYBACK_STREAM, + PA_COMMAND_STAT, + PA_COMMAND_GET_PLAYBACK_LATENCY, + PA_COMMAND_CREATE_UPLOAD_STREAM, + PA_COMMAND_DELETE_UPLOAD_STREAM, + PA_COMMAND_FINISH_UPLOAD_STREAM, + PA_COMMAND_PLAY_SAMPLE, + PA_COMMAND_REMOVE_SAMPLE, + + PA_COMMAND_GET_SERVER_INFO, + PA_COMMAND_GET_SINK_INFO, + PA_COMMAND_GET_SINK_INFO_LIST, + PA_COMMAND_GET_SOURCE_INFO, + PA_COMMAND_GET_SOURCE_INFO_LIST, + PA_COMMAND_GET_MODULE_INFO, + PA_COMMAND_GET_MODULE_INFO_LIST, + PA_COMMAND_GET_CLIENT_INFO, + PA_COMMAND_GET_CLIENT_INFO_LIST, + PA_COMMAND_GET_SINK_INPUT_INFO, + PA_COMMAND_GET_SINK_INPUT_INFO_LIST, + PA_COMMAND_GET_SOURCE_OUTPUT_INFO, + PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, + PA_COMMAND_GET_SAMPLE_INFO, + PA_COMMAND_GET_SAMPLE_INFO_LIST, + PA_COMMAND_SUBSCRIBE, + + PA_COMMAND_SET_SINK_VOLUME, + PA_COMMAND_SET_SINK_INPUT_VOLUME, + PA_COMMAND_SET_SOURCE_VOLUME, + + PA_COMMAND_SET_SINK_MUTE, + PA_COMMAND_SET_SOURCE_MUTE, + + PA_COMMAND_CORK_PLAYBACK_STREAM, + PA_COMMAND_FLUSH_PLAYBACK_STREAM, + PA_COMMAND_TRIGGER_PLAYBACK_STREAM, + + PA_COMMAND_SET_DEFAULT_SINK, + PA_COMMAND_SET_DEFAULT_SOURCE, + + PA_COMMAND_SET_PLAYBACK_STREAM_NAME, + PA_COMMAND_SET_RECORD_STREAM_NAME, + + PA_COMMAND_KILL_CLIENT, + PA_COMMAND_KILL_SINK_INPUT, + PA_COMMAND_KILL_SOURCE_OUTPUT, + PA_COMMAND_LOAD_MODULE, + PA_COMMAND_UNLOAD_MODULE, + PA_COMMAND_ADD_AUTOLOAD, + PA_COMMAND_REMOVE_AUTOLOAD, + PA_COMMAND_GET_AUTOLOAD_INFO, + PA_COMMAND_GET_AUTOLOAD_INFO_LIST, + PA_COMMAND_GET_RECORD_LATENCY, + PA_COMMAND_CORK_RECORD_STREAM, + PA_COMMAND_FLUSH_RECORD_STREAM, + PA_COMMAND_PREBUF_PLAYBACK_STREAM, + + /* Commands from server to client */ + PA_COMMAND_REQUEST, + PA_COMMAND_OVERFLOW, + PA_COMMAND_UNDERFLOW, + PA_COMMAND_PLAYBACK_STREAM_KILLED, + PA_COMMAND_RECORD_STREAM_KILLED, + PA_COMMAND_SUBSCRIBE_EVENT, + + PA_COMMAND_MAX +}; + +#define PA_NATIVE_COOKIE_LENGTH 256 +#define PA_NATIVE_COOKIE_FILE ".pulseaudio-cookie" + +#define PA_NATIVE_DEFAULT_PORT 4713 + +#define PA_NATIVE_COOKIE_PROPERTY_NAME "protocol-native-cookie" +#define PA_NATIVE_SERVER_PROPERTY_NAME "protocol-native-server" + +#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native" + + +PA_C_DECL_END + +#endif diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c new file mode 100644 index 00000000..8b010f01 --- /dev/null +++ b/src/pulsecore/packet.c @@ -0,0 +1,79 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> + +#include <pulse/xmalloc.h> + +#include "packet.h" + +pa_packet* pa_packet_new(size_t length) { + pa_packet *p; + + assert(length); + + p = pa_xmalloc(sizeof(pa_packet)+length); + p->ref = 1; + p->length = length; + p->data = (uint8_t*) (p+1); + p->type = PA_PACKET_APPENDED; + + return p; +} + +pa_packet* pa_packet_new_dynamic(void* data, size_t length) { + pa_packet *p; + + assert(data); + assert(length); + + p = pa_xnew(pa_packet, 1); + p->ref = 1; + p->length = length; + p->data = data; + p->type = PA_PACKET_DYNAMIC; + + return p; +} + +pa_packet* pa_packet_ref(pa_packet *p) { + assert(p); + assert(p->ref >= 1); + + p->ref++; + return p; +} + +void pa_packet_unref(pa_packet *p) { + assert(p); + assert(p->ref >= 1); + + if (--p->ref == 0) { + if (p->type == PA_PACKET_DYNAMIC) + pa_xfree(p->data); + pa_xfree(p); + } +} diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h new file mode 100644 index 00000000..7842857a --- /dev/null +++ b/src/pulsecore/packet.h @@ -0,0 +1,41 @@ +#ifndef foopackethfoo +#define foopackethfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <sys/types.h> +#include <inttypes.h> + +typedef struct pa_packet { + enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type; + unsigned ref; + size_t length; + uint8_t *data; +} pa_packet; + +pa_packet* pa_packet_new(size_t length); +pa_packet* pa_packet_new_dynamic(void* data, size_t length); + +pa_packet* pa_packet_ref(pa_packet *p); +void pa_packet_unref(pa_packet *p); + +#endif diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c new file mode 100644 index 00000000..da1647af --- /dev/null +++ b/src/pulsecore/parseaddr.c @@ -0,0 +1,115 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <assert.h> +#include <stdlib.h> + +#include <pulse/xmalloc.h> + +#include <pulse/util.h> +#include <pulsecore/core-util.h> + +#include "parseaddr.h" + +/* Parse addresses in one of the following forms: + * HOSTNAME + * HOSTNAME:PORT + * [HOSTNAME] + * [HOSTNAME]:PORT + * + * Return a newly allocated string of the hostname and fill in *ret_port if specified */ + +static char *parse_host(const char *s, uint16_t *ret_port) { + assert(s && ret_port); + if (*s == '[') { + char *e; + if (!(e = strchr(s+1, ']'))) + return NULL; + + if (e[1] == ':') + *ret_port = atoi(e+2); + else if (e[1] != 0) + return NULL; + + return pa_xstrndup(s+1, e-s-1); + } else { + char *e; + + if (!(e = strrchr(s, ':'))) + return pa_xstrdup(s); + + *ret_port = atoi(e+1); + return pa_xstrndup(s, e-s); + } +} + +int pa_parse_address(const char *name, pa_parsed_address *ret_p) { + const char *p; + assert(name && ret_p); + memset(ret_p, 0, sizeof(pa_parsed_address)); + ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO; + + if (*name == '{') { + char hn[256], *pfx; + /* The URL starts with a host specification for detecting local connections */ + + if (!pa_get_host_name(hn, sizeof(hn))) + return -1; + + pfx = pa_sprintf_malloc("{%s}", hn); + if (!pa_startswith(name, pfx)) { + pa_xfree(pfx); + /* Not local */ + return -1; + } + + p = name + strlen(pfx); + pa_xfree(pfx); + } else + p = name; + + if (*p == '/') + ret_p->type = PA_PARSED_ADDRESS_UNIX; + else if (pa_startswith(p, "unix:")) { + ret_p->type = PA_PARSED_ADDRESS_UNIX; + p += sizeof("unix:")-1; + } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) { + ret_p->type = PA_PARSED_ADDRESS_TCP4; + p += sizeof("tcp:")-1; + } else if (pa_startswith(p, "tcp6:")) { + ret_p->type = PA_PARSED_ADDRESS_TCP6; + p += sizeof("tcp6:")-1; + } + + if (ret_p->type == PA_PARSED_ADDRESS_UNIX) + ret_p->path_or_host = pa_xstrdup(p); + else + if (!(ret_p->path_or_host = parse_host(p, &ret_p->port))) + return -1; + + + return 0; +} diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h new file mode 100644 index 00000000..0393f665 --- /dev/null +++ b/src/pulsecore/parseaddr.h @@ -0,0 +1,42 @@ +#ifndef fooparseaddrhfoo +#define fooparseaddrhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> + +typedef enum pa_parsed_address_type { + PA_PARSED_ADDRESS_UNIX, + PA_PARSED_ADDRESS_TCP4, + PA_PARSED_ADDRESS_TCP6, + PA_PARSED_ADDRESS_TCP_AUTO +} pa_parsed_address_type_t; + +typedef struct pa_parsed_address { + pa_parsed_address_type_t type; + char *path_or_host; + uint16_t port; +} pa_parsed_address; + +int pa_parse_address(const char *a, pa_parsed_address *ret_p); + +#endif diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c new file mode 100644 index 00000000..5b76b432 --- /dev/null +++ b/src/pulsecore/pdispatch.c @@ -0,0 +1,318 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include <pulse/timeval.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/native-common.h> +#include <pulsecore/llist.h> +#include <pulsecore/log.h> +#include <pulsecore/core-util.h> + +#include "pdispatch.h" + +/*#define DEBUG_OPCODES */ + +#ifdef DEBUG_OPCODES + +static const char *command_names[PA_COMMAND_MAX] = { + [PA_COMMAND_ERROR] = "ERROR", + [PA_COMMAND_TIMEOUT] = "TIMEOUT", + [PA_COMMAND_REPLY] = "REPLY", + [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM", + [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM", + [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM", + [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM", + [PA_COMMAND_AUTH] = "AUTH", + [PA_COMMAND_REQUEST] = "REQUEST", + [PA_COMMAND_EXIT] = "EXIT", + [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME", + [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK", + [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE", + [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM", + [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED", + [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED", + [PA_COMMAND_STAT] = "STAT", + [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY", + [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM", + [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM", + [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM", + [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE", + [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE", + [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO", + [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO", + [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST", + [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO", + [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST", + [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO", + [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST", + [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO", + [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST", + [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO", + [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST", + [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO", + [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST", + [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO", + [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST", + [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE", + [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT", + [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME", + [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME", + [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLME", + [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM", + [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM", + [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM", + [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO", + [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST", +}; + +#endif + +struct reply_info { + pa_pdispatch *pdispatch; + PA_LLIST_FIELDS(struct reply_info); + pa_pdispatch_cb_t callback; + void *userdata; + pa_free_cb_t free_cb; + uint32_t tag; + pa_time_event *time_event; +}; + +struct pa_pdispatch { + int ref; + pa_mainloop_api *mainloop; + const pa_pdispatch_cb_t *callback_table; + unsigned n_commands; + PA_LLIST_HEAD(struct reply_info, replies); + pa_pdispatch_drain_callback drain_callback; + void *drain_userdata; + const void *creds; +}; + +static void reply_info_free(struct reply_info *r) { + assert(r && r->pdispatch && r->pdispatch->mainloop); + + if (r->time_event) + r->pdispatch->mainloop->time_free(r->time_event); + + PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r); + + pa_xfree(r); +} + +pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) { + pa_pdispatch *pd; + assert(mainloop); + + assert((entries && table) || (!entries && !table)); + + pd = pa_xmalloc(sizeof(pa_pdispatch)); + pd->ref = 1; + pd->mainloop = mainloop; + pd->callback_table = table; + pd->n_commands = entries; + PA_LLIST_HEAD_INIT(pa_reply_info, pd->replies); + pd->drain_callback = NULL; + pd->drain_userdata = NULL; + pd->creds = NULL; + + return pd; +} + +static void pdispatch_free(pa_pdispatch *pd) { + assert(pd); + + while (pd->replies) { + if (pd->replies->free_cb) + pd->replies->free_cb(pd->replies->userdata); + + reply_info_free(pd->replies); + } + + pa_xfree(pd); +} + +static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) { + pa_pdispatch_cb_t callback; + void *userdata; + uint32_t tag; + assert(r); + + pa_pdispatch_ref(pd); + + callback = r->callback; + userdata = r->userdata; + tag = r->tag; + + reply_info_free(r); + + callback(pd, command, tag, ts, userdata); + + if (pd->drain_callback && !pa_pdispatch_is_pending(pd)) + pd->drain_callback(pd, pd->drain_userdata); + + pa_pdispatch_unref(pd); +} + +int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const void *creds, void *userdata) { + uint32_t tag, command; + pa_tagstruct *ts = NULL; + int ret = -1; + assert(pd && packet && packet->data); + + pa_pdispatch_ref(pd); + + if (packet->length <= 8) + goto finish; + + ts = pa_tagstruct_new(packet->data, packet->length); + assert(ts); + + if (pa_tagstruct_getu32(ts, &command) < 0 || + pa_tagstruct_getu32(ts, &tag) < 0) + goto finish; + +#ifdef DEBUG_OPCODES +{ + char t[256]; + char const *p; + if (!(p = command_names[command])) + snprintf((char*) (p = t), sizeof(t), "%u", command); + + pa_log(__FILE__": Recieved opcode <%s>", p); +} +#endif + + pd->creds = creds; + + if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) { + struct reply_info *r; + + for (r = pd->replies; r; r = r->next) + if (r->tag == tag) + break; + + if (r) + run_action(pd, r, command, ts); + + } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) { + const pa_pdispatch_cb_t *c = pd->callback_table+command; + + (*c)(pd, command, tag, ts, userdata); + } else { + pa_log(__FILE__": Recieved unsupported command %u", command); + goto finish; + } + + ret = 0; + +finish: + pd->creds = NULL; + + if (ts) + pa_tagstruct_free(ts); + + pa_pdispatch_unref(pd); + + return ret; +} + +static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + struct reply_info*r = userdata; + assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback); + + run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL); +} + +void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) { + struct reply_info *r; + struct timeval tv; + assert(pd && pd->ref >= 1 && cb); + + r = pa_xmalloc(sizeof(struct reply_info)); + r->pdispatch = pd; + r->callback = cb; + r->userdata = userdata; + r->free_cb = free_cb; + r->tag = tag; + + pa_gettimeofday(&tv); + tv.tv_sec += timeout; + + r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r); + assert(r->time_event); + + PA_LLIST_PREPEND(struct reply_info, pd->replies, r); +} + +int pa_pdispatch_is_pending(pa_pdispatch *pd) { + assert(pd); + + return !!pd->replies; +} + +void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) { + assert(pd); + assert(!cb || pa_pdispatch_is_pending(pd)); + + pd->drain_callback = cb; + pd->drain_userdata = userdata; +} + +void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) { + struct reply_info *r, *n; + assert(pd); + + for (r = pd->replies; r; r = n) { + n = r->next; + + if (r->userdata == userdata) + reply_info_free(r); + } +} + +void pa_pdispatch_unref(pa_pdispatch *pd) { + assert(pd && pd->ref >= 1); + + if (!(--(pd->ref))) + pdispatch_free(pd); +} + +pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) { + assert(pd && pd->ref >= 1); + pd->ref++; + return pd; +} + +const void * pa_pdispatch_creds(pa_pdispatch *pd) { + assert(pd); + assert(pd->ref >= 1); + + return pd->creds; +} diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h new file mode 100644 index 00000000..07620e5a --- /dev/null +++ b/src/pulsecore/pdispatch.h @@ -0,0 +1,53 @@ +#ifndef foopdispatchhfoo +#define foopdispatchhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> +#include <pulse/mainloop-api.h> +#include <pulse/def.h> +#include <pulsecore/tagstruct.h> +#include <pulsecore/packet.h> + +typedef struct pa_pdispatch pa_pdispatch; + +typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata); + +pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_cb_t*table, unsigned entries); +void pa_pdispatch_unref(pa_pdispatch *pd); +pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd); + +int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, const void*creds, void *userdata); + +void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t callback, void *userdata, pa_free_cb_t free_cb); + +int pa_pdispatch_is_pending(pa_pdispatch *pd); + +void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata); + +/* Remove all reply slots with the give userdata parameter */ +void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata); + +const void * pa_pdispatch_creds(pa_pdispatch *pd); + +#endif diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c new file mode 100644 index 00000000..b8972866 --- /dev/null +++ b/src/pulsecore/pid.c @@ -0,0 +1,304 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <limits.h> +#include <signal.h> + +#ifdef HAVE_WINDOWS_H +#include <windows.h> +#endif + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/core-util.h> +#include <pulsecore/log.h> + +#include "pid.h" + +/* Read the PID data from the file descriptor fd, and return it. If no + * pid could be read, return 0, on failure (pid_t) -1 */ +static pid_t read_pid(const char *fn, int fd) { + ssize_t r; + char t[20], *e; + uint32_t pid; + + assert(fn && fd >= 0); + + if ((r = pa_loop_read(fd, t, sizeof(t)-1)) < 0) { + pa_log_warn(__FILE__": WARNING: failed to read PID file '%s': %s", + fn, pa_cstrerror(errno)); + return (pid_t) -1; + } + + if (r == 0) + return (pid_t) 0; + + t[r] = 0; + if ((e = strchr(t, '\n'))) + *e = 0; + + if (pa_atou(t, &pid) < 0) { + pa_log(__FILE__": WARNING: failed to parse PID file '%s'", fn); + return (pid_t) -1; + } + + return (pid_t) pid; +} + +static int open_pid_file(const char *fn, int mode) { + int fd = -1; + int lock = -1; + + for (;;) { + struct stat st; + + pa_make_secure_parent_dir(fn); + + if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) { + if (mode != O_RDONLY || errno != ENOENT) + pa_log_warn(__FILE__": WARNING: failed to open PID file '%s': %s", + fn, pa_cstrerror(errno)); + goto fail; + } + + /* Try to lock the file. If that fails, go without */ + if (pa_lock_fd(fd, 1) < 0) + goto fail; + + if (fstat(fd, &st) < 0) { + pa_log_warn(__FILE__": WARNING: failed to fstat() PID file '%s': %s", + fn, pa_cstrerror(errno)); + goto fail; + } + + /* Does the file still exist in the file system? When ye, w're done, otherwise restart */ + if (st.st_nlink >= 1) + break; + + if (pa_lock_fd(fd, 0) < 0) + goto fail; + + if (close(fd) < 0) { + pa_log_warn(__FILE__": WARNING: failed to close file '%s': %s", + fn, pa_cstrerror(errno)); + goto fail; + } + + fd = -1; + } + + return fd; + +fail: + + if (fd < 0) { + if (lock >= 0) + pa_lock_fd(fd, 0); + + close(fd); + } + + return -1; +} + +/* Create a new PID file for the current process. */ +int pa_pid_file_create(void) { + int fd = -1; + int ret = -1; + char fn[PATH_MAX]; + char t[20]; + pid_t pid; + size_t l; + +#ifdef OS_IS_WIN32 + HANDLE process; +#endif + + pa_runtime_path("pid", fn, sizeof(fn)); + + if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0) + goto fail; + + if ((pid = read_pid(fn, fd)) == (pid_t) -1) + pa_log(__FILE__": corrupt PID file, overwriting."); + else if (pid > 0) { +#ifdef OS_IS_WIN32 + if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) { + CloseHandle(process); +#else + if (kill(pid, 0) >= 0 || errno != ESRCH) { +#endif + pa_log(__FILE__": daemon already running."); + goto fail; + } + + pa_log(__FILE__": stale PID file, overwriting."); + } + + /* Overwrite the current PID file */ + if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) { + pa_log(__FILE__": failed to truncate PID file '%s': %s", + fn, pa_cstrerror(errno)); + goto fail; + } + + snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid()); + l = strlen(t); + + if (pa_loop_write(fd, t, l) != (ssize_t) l) { + pa_log(__FILE__": failed to write PID file."); + goto fail; + } + + ret = 0; + +fail: + if (fd >= 0) { + pa_lock_fd(fd, 0); + close(fd); + } + + return ret; +} + +/* Remove the PID file, if it is ours */ +int pa_pid_file_remove(void) { + int fd = -1; + char fn[PATH_MAX]; + int ret = -1; + pid_t pid; + char *p; + + pa_runtime_path("pid", fn, sizeof(fn)); + + if ((fd = open_pid_file(fn, O_RDWR)) < 0) { + pa_log_warn(__FILE__": WARNING: failed to open PID file '%s': %s", + fn, pa_cstrerror(errno)); + goto fail; + } + + if ((pid = read_pid(fn, fd)) == (pid_t) -1) + goto fail; + + if (pid != getpid()) { + pa_log(__FILE__": WARNING: PID file '%s' not mine!", fn); + goto fail; + } + + if (ftruncate(fd, 0) < 0) { + pa_log_warn(__FILE__": WARNING: failed to truncate PID file '%s': %s", + fn, pa_cstrerror(errno)); + goto fail; + } + +#ifdef OS_IS_WIN32 + pa_lock_fd(fd, 0); + close(fd); + fd = -1; +#endif + + if (unlink(fn) < 0) { + pa_log_warn(__FILE__": WARNING: failed to remove PID file '%s': %s", + fn, pa_cstrerror(errno)); + goto fail; + } + + if ((p = pa_parent_dir(fn))) { + rmdir(p); + pa_xfree(p); + } + + ret = 0; + +fail: + + if (fd >= 0) { + pa_lock_fd(fd, 0); + close(fd); + } + + return ret; +} + +/* Check whether the daemon is currently running, i.e. if a PID file + * exists and the PID therein too. Returns 0 on succcess, -1 + * otherwise. If pid is non-NULL and a running daemon was found, + * return its PID therein */ +int pa_pid_file_check_running(pid_t *pid) { + return pa_pid_file_kill(0, pid); +} + +#ifndef OS_IS_WIN32 + +/* Kill a current running daemon. Return non-zero on success, -1 + * otherwise. If successful *pid contains the PID of the daemon + * process. */ +int pa_pid_file_kill(int sig, pid_t *pid) { + int fd = -1; + char fn[PATH_MAX]; + int ret = -1; + pid_t _pid; + + if (!pid) + pid = &_pid; + + pa_runtime_path("pid", fn, sizeof(fn)); + + if ((fd = open_pid_file(fn, O_RDONLY)) < 0) + goto fail; + + if ((*pid = read_pid(fn, fd)) == (pid_t) -1) + goto fail; + + ret = kill(*pid, sig); + +fail: + + if (fd >= 0) { + pa_lock_fd(fd, 0); + close(fd); + } + + return ret; + +} + +#else /* OS_IS_WIN32 */ + +int pa_pid_file_kill(int sig, pid_t *pid) { + return -1; +} + +#endif diff --git a/src/pulsecore/pid.h b/src/pulsecore/pid.h new file mode 100644 index 00000000..bd476b29 --- /dev/null +++ b/src/pulsecore/pid.h @@ -0,0 +1,30 @@ +#ifndef foopidhfoo +#define foopidhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +int pa_pid_file_create(void); +int pa_pid_file_remove(void); +int pa_pid_file_check_running(pid_t *pid); +int pa_pid_file_kill(int sig, pid_t *pid); + +#endif diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c new file mode 100644 index 00000000..6933e180 --- /dev/null +++ b/src/pulsecore/pipe.c @@ -0,0 +1,160 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with PulseAudio; If not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/types.h> + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "winsock.h" + +#include "pipe.h" + +#ifndef HAVE_PIPE + +static int set_block(int fd, int blocking) { +#ifdef O_NONBLOCK + + int v; + + assert(fd >= 0); + + if ((v = fcntl(fd, F_GETFL)) < 0) + return -1; + + if (blocking) + v &= ~O_NONBLOCK; + else + v |= O_NONBLOCK; + + if (fcntl(fd, F_SETFL, v) < 0) + return -1; + + return 0; + +#elif defined(OS_IS_WIN32) + + u_long arg; + + arg = !blocking; + + if (ioctlsocket(fd, FIONBIO, &arg) < 0) + return -1; + + return 0; + +#else + + return -1; + +#endif +} + +int pipe(int filedes[2]) { + int listener; + struct sockaddr_in addr, peer; + socklen_t len; + + listener = -1; + filedes[0] = -1; + filedes[1] = -1; + + listener = socket(PF_INET, SOCK_STREAM, 0); + if (listener < 0) + goto error; + + filedes[0] = socket(PF_INET, SOCK_STREAM, 0); + if (filedes[0] < 0) + goto error; + + filedes[1] = socket(PF_INET, SOCK_STREAM, 0); + if (filedes[1] < 0) + goto error; + + /* Make non-blocking so that connect() won't block */ + if (set_block(filedes[0], 0) < 0) + goto error; + + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) < 0) + goto error; + + if (listen(listener, 1) < 0) + goto error; + + len = sizeof(addr); + if (getsockname(listener, (struct sockaddr*)&addr, &len) < 0) + goto error; + + if (connect(filedes[0], (struct sockaddr*)&addr, sizeof(addr)) < 0) { +#ifdef OS_IS_WIN32 + if (WSAGetLastError() != EWOULDBLOCK) +#else + if (errno != EINPROGRESS) +#endif + goto error; + } + + len = sizeof(peer); + filedes[1] = accept(listener, (struct sockaddr*)&peer, &len); + if (filedes[1] < 0) + goto error; + + /* Restore blocking */ + if (set_block(filedes[0], 1) < 0) + goto error; + + len = sizeof(addr); + if (getsockname(filedes[0], (struct sockaddr*)&addr, &len) < 0) + goto error; + + /* Check that someone else didn't steal the connection */ + if ((addr.sin_port != peer.sin_port) || (addr.sin_addr.s_addr != peer.sin_addr.s_addr)) + goto error; + + close(listener); + + return 0; + +error: + if (listener >= 0) + close(listener); + if (filedes[0] >= 0) + close(filedes[0]); + if (filedes[1] >= 0) + close(filedes[0]); + + return -1; +} + +#endif /* HAVE_PIPE */ diff --git a/src/pulsecore/pipe.h b/src/pulsecore/pipe.h new file mode 100644 index 00000000..a2514760 --- /dev/null +++ b/src/pulsecore/pipe.h @@ -0,0 +1,31 @@ +#ifndef foopipehfoo +#define foopipehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with PulseAudio; If not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +***/ + +#ifndef HAVE_PIPE + +int pipe(int filedes[2]); + +#endif + +#endif diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c new file mode 100644 index 00000000..7ac579e9 --- /dev/null +++ b/src/pulsecore/play-memchunk.c @@ -0,0 +1,117 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/sink-input.h> +#include <pulsecore/gccmacro.h> + +#include "play-memchunk.h" + +static void sink_input_kill(pa_sink_input *i) { + pa_memchunk *c; + assert(i && i->userdata); + c = i->userdata; + + pa_sink_input_disconnect(i); + pa_sink_input_unref(i); + + pa_memblock_unref(c->memblock); + pa_xfree(c); +} + +static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { + pa_memchunk *c; + assert(i && chunk && i->userdata); + c = i->userdata; + + if (c->length <= 0) + return -1; + + assert(c->memblock && c->memblock->length); + *chunk = *c; + pa_memblock_ref(c->memblock); + + return 0; +} + +static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) { + sink_input_kill(i); +} + +static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) { + pa_memchunk *c; + assert(i && length && i->userdata); + c = i->userdata; + + assert(!memcmp(chunk, c, sizeof(chunk))); + assert(length <= c->length); + + c->length -= length; + c->index += length; + + if (c->length <= 0) + pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); +} + +int pa_play_memchunk( + pa_sink *sink, + const char *name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_memchunk *chunk, + pa_cvolume *cvolume) { + + pa_sink_input *si; + pa_memchunk *nchunk; + + assert(sink); + assert(ss); + assert(chunk); + + if (cvolume && pa_cvolume_is_muted(cvolume)) + return 0; + + if (!(si = pa_sink_input_new(sink, name, __FILE__, ss, map, cvolume, 0, PA_RESAMPLER_INVALID))) + return -1; + + si->peek = sink_input_peek; + si->drop = sink_input_drop; + si->kill = sink_input_kill; + + si->userdata = nchunk = pa_xnew(pa_memchunk, 1); + *nchunk = *chunk; + + pa_memblock_ref(chunk->memblock); + + pa_sink_notify(sink); + + return 0; +} diff --git a/src/pulsecore/play-memchunk.h b/src/pulsecore/play-memchunk.h new file mode 100644 index 00000000..3d5b8cc6 --- /dev/null +++ b/src/pulsecore/play-memchunk.h @@ -0,0 +1,36 @@ +#ifndef fooplaychunkhfoo +#define fooplaychunkhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/sink.h> +#include <pulsecore/memchunk.h> + +int pa_play_memchunk( + pa_sink *sink, + const char *name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_memchunk *chunk, + pa_cvolume *cvolume); + +#endif diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c new file mode 100644 index 00000000..3f9ace7c --- /dev/null +++ b/src/pulsecore/poll.c @@ -0,0 +1,191 @@ +/* $Id$ */ + +/*** + Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 2005, Cendio AB. + This file is part of PulseAudio. + Based on work for the GNU C Library. + + PulseAudio is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with PulseAudio; If not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +***/ + +/* Poll the file descriptors described by the NFDS structures starting at + FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for + an event to occur; if TIMEOUT is -1, block until an event occurs. + Returns the number of file descriptors with events, zero if timed out, + or -1 for errors. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#include "winsock.h" + +#ifndef HAVE_SYS_POLL_H + +#include <pulsecore/core-util.h> + +#include "poll.h" + +int poll (struct pollfd *fds, unsigned long int nfds, int timeout) { + struct timeval tv; + fd_set rset, wset, xset; + struct pollfd *f; + int ready; + int maxfd = 0; + char data[64]; + + FD_ZERO (&rset); + FD_ZERO (&wset); + FD_ZERO (&xset); + + if (nfds == 0) { + if (timeout >= 0) { + pa_msleep(timeout); + return 0; + } + +#ifdef OS_IS_WIN32 + /* + * Windows does not support signals properly so waiting for them would + * mean a deadlock. + */ + pa_msleep(100); + return 0; +#else + return select(0, NULL, NULL, NULL, NULL); +#endif + } + + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd != -1) { + if (f->events & POLLIN) + FD_SET (f->fd, &rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &xset); + if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) + maxfd = f->fd; + } + } + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, + SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, + SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); + if ((ready == -1) && (errno == EBADF)) { + ready = 0; + + FD_ZERO (&rset); + FD_ZERO (&wset); + FD_ZERO (&xset); + + maxfd = -1; + + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd != -1) { + fd_set sngl_rset, sngl_wset, sngl_xset; + + FD_ZERO (&sngl_rset); + FD_ZERO (&sngl_wset); + FD_ZERO (&sngl_xset); + + if (f->events & POLLIN) + FD_SET (f->fd, &sngl_rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &sngl_wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &sngl_xset); + if (f->events & (POLLIN|POLLOUT|POLLPRI)) { + struct timeval singl_tv; + + singl_tv.tv_sec = 0; + singl_tv.tv_usec = 0; + + if (select((SELECT_TYPE_ARG1) f->fd, SELECT_TYPE_ARG234 &rset, + SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, + SELECT_TYPE_ARG5 &singl_tv) != -1) { + if (f->events & POLLIN) + FD_SET (f->fd, &rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &xset); + if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) + maxfd = f->fd; + ++ready; + } else if (errno == EBADF) + f->revents |= POLLNVAL; + } + } + } + + if (ready) { + /* Linux alters the tv struct... but it shouldn't matter here ... + * as we're going to be a little bit out anyway as we've just eaten + * more than a couple of cpu cycles above */ + ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, + SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, + SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); + } + } + +#ifdef OS_IS_WIN32 + errno = WSAGetLastError(); +#endif + + if (ready > 0) { + ready = 0; + for (f = fds; f < &fds[nfds]; ++f) { + f->revents = 0; + if (f->fd != -1) { + if (FD_ISSET (f->fd, &rset)) { + /* support for POLLHUP. An hung up descriptor does not + increase the return value! */ + if (recv (f->fd, data, 64, MSG_PEEK) == -1) { + if (errno == ESHUTDOWN || errno == ECONNRESET || + errno == ECONNABORTED || errno == ENETRESET) { + fprintf(stderr, "Hangup\n"); + f->revents |= POLLHUP; + } + } + + if (f->revents == 0) + f->revents |= POLLIN; + } + if (FD_ISSET (f->fd, &wset)) + f->revents |= POLLOUT; + if (FD_ISSET (f->fd, &xset)) + f->revents |= POLLPRI; + } + if (f->revents) + ready++; + } + } + + return ready; +} + +#endif /* HAVE_SYS_POLL_H */ diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h new file mode 100644 index 00000000..bed772c7 --- /dev/null +++ b/src/pulsecore/poll.h @@ -0,0 +1,57 @@ +/* $Id$ */ + +/*** + Compatibility definitions for System V `poll' interface. + Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc. + Copyright (C) 2005, Cendio AB. + This file is part of PulseAudio. + Based on work for the GNU C Library. + + PulseAudio is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with PulseAudio; If not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +***/ + +/* Event types that can be polled for. These bits may be set in `events' + to indicate the interesting event types; they will appear in `revents' + to indicate the status of the file descriptor. */ +#define POLLIN 0x001 /* There is data to read. */ +#define POLLPRI 0x002 /* There is urgent data to read. */ +#define POLLOUT 0x004 /* Writing now will not block. */ + +/* Event types always implicitly polled for. These bits need not be set in + `events', but they will appear in `revents' to indicate the status of + the file descriptor. */ +#define POLLERR 0x008 /* Error condition. */ +#define POLLHUP 0x010 /* Hung up. */ +#define POLLNVAL 0x020 /* Invalid polling request. */ + + +/* Type used for the number of file descriptors. */ +typedef unsigned long int nfds_t; + +/* Data structure describing a polling request. */ +struct pollfd + { + int fd; /* File descriptor to poll. */ + short int events; /* Types of events poller cares about. */ + short int revents; /* Types of events that actually occurred. */ + }; + +/* Poll the file descriptors described by the NFDS structures starting at + FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for + an event to occur; if TIMEOUT is -1, block until an event occurs. + Returns the number of file descriptors with events, zero if timed out, + or -1 for errors. */ +extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout); diff --git a/src/pulsecore/props.c b/src/pulsecore/props.c new file mode 100644 index 00000000..8879b7aa --- /dev/null +++ b/src/pulsecore/props.c @@ -0,0 +1,121 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <assert.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/log.h> + +#include "props.h" + +typedef struct pa_property { + char *name; /* Points to memory allocated by the property subsystem */ + void *data; /* Points to memory maintained by the caller */ +} pa_property; + +/* Allocate a new property object */ +static pa_property* property_new(const char *name, void *data) { + pa_property* p; + assert(name && data); + + p = pa_xmalloc(sizeof(pa_property)); + p->name = pa_xstrdup(name); + p->data = data; + + return p; +} + +/* Free a property object */ +static void property_free(pa_property *p) { + assert(p); + + pa_xfree(p->name); + pa_xfree(p); +} + +void* pa_property_get(pa_core *c, const char *name) { + pa_property *p; + assert(c && name && c->properties); + + if (!(p = pa_hashmap_get(c->properties, name))) + return NULL; + + return p->data; +} + +int pa_property_set(pa_core *c, const char *name, void *data) { + pa_property *p; + assert(c && name && data && c->properties); + + if (pa_hashmap_get(c->properties, name)) + return -1; + + p = property_new(name, data); + pa_hashmap_put(c->properties, p->name, p); + return 0; +} + +int pa_property_remove(pa_core *c, const char *name) { + pa_property *p; + assert(c && name && c->properties); + + if (!(p = pa_hashmap_remove(c->properties, name))) + return -1; + + property_free(p); + return 0; +} + +void pa_property_init(pa_core *c) { + assert(c); + + c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); +} + +void pa_property_cleanup(pa_core *c) { + assert(c); + + if (!c->properties) + return; + + assert(!pa_hashmap_size(c->properties)); + + pa_hashmap_free(c->properties, NULL, NULL); + c->properties = NULL; + +} + +void pa_property_dump(pa_core *c, pa_strbuf *s) { + void *state = NULL; + pa_property *p; + assert(c && s); + + while ((p = pa_hashmap_iterate(c->properties, &state, NULL))) + pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data); +} + +int pa_property_replace(pa_core *c, const char *name, void *data) { + assert(c && name); + + pa_property_remove(c, name); + return pa_property_set(c, name, data); +} diff --git a/src/pulsecore/props.h b/src/pulsecore/props.h new file mode 100644 index 00000000..39b7ca68 --- /dev/null +++ b/src/pulsecore/props.h @@ -0,0 +1,58 @@ +#ifndef foopropshfoo +#define foopropshfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/core.h> +#include <pulsecore/strbuf.h> + +/* The property subsystem is to be used to share data between + * modules. Consider them to be kind of "global" variables for a + * core. Why not use the hashmap functions directly? The hashmap + * functions copy neither the key nor value, while this property + * system copies the key. Users of this system have to think about + * reference counting themselves. */ + +/* Return a pointer to the value of the specified property. */ +void* pa_property_get(pa_core *c, const char *name); + +/* Set the property 'name' to 'data'. This function fails in case a + * property by this name already exists. The property data is not + * copied or reference counted. This is the caller's job. */ +int pa_property_set(pa_core *c, const char *name, void *data); + +/* Remove the specified property. Return non-zero on failure */ +int pa_property_remove(pa_core *c, const char *name); + +/* A combination of pa_property_remove() and pa_property_set() */ +int pa_property_replace(pa_core *c, const char *name, void *data); + +/* Free all memory used by the property system */ +void pa_property_cleanup(pa_core *c); + +/* Initialize the properties subsystem */ +void pa_property_init(pa_core *c); + +/* Dump the current set of properties */ +void pa_property_dump(pa_core *c, pa_strbuf *s); + +#endif diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c new file mode 100644 index 00000000..c5854f1e --- /dev/null +++ b/src/pulsecore/protocol-cli.c @@ -0,0 +1,97 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/cli.h> +#include <pulsecore/log.h> + +#include "protocol-cli.h" + +/* Don't allow more than this many concurrent connections */ +#define MAX_CONNECTIONS 25 + +struct pa_protocol_cli { + pa_module *module; + pa_core *core; + pa_socket_server*server; + pa_idxset *connections; +}; + +static void cli_eof_cb(pa_cli*c, void*userdata) { + pa_protocol_cli *p = userdata; + assert(p); + pa_idxset_remove_by_data(p->connections, c, NULL); + pa_cli_free(c); +} + +static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { + pa_protocol_cli *p = userdata; + pa_cli *c; + assert(s && io && p); + + if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { + pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); + pa_iochannel_free(io); + return; + } + + c = pa_cli_new(p->core, io, p->module); + assert(c); + pa_cli_set_eof_callback(c, cli_eof_cb, p); + + pa_idxset_put(p->connections, c, NULL); +} + +pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) { + pa_protocol_cli* p; + assert(core && server); + + p = pa_xmalloc(sizeof(pa_protocol_cli)); + p->module = m; + p->core = core; + p->server = server; + p->connections = pa_idxset_new(NULL, NULL); + + pa_socket_server_set_callback(p->server, on_connection, p); + + return p; +} + +static void free_connection(void *p, PA_GCC_UNUSED void *userdata) { + assert(p); + pa_cli_free(p); +} + +void pa_protocol_cli_free(pa_protocol_cli *p) { + assert(p); + + pa_idxset_free(p->connections, free_connection, NULL); + pa_socket_server_unref(p->server); + pa_xfree(p); +} diff --git a/src/pulsecore/protocol-cli.h b/src/pulsecore/protocol-cli.h new file mode 100644 index 00000000..84101e14 --- /dev/null +++ b/src/pulsecore/protocol-cli.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolclihfoo +#define fooprotocolclihfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/core.h> +#include <pulsecore/socket-server.h> +#include <pulsecore/module.h> +#include <pulsecore/modargs.h> + +typedef struct pa_protocol_cli pa_protocol_cli; + +pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma); +void pa_protocol_cli_free(pa_protocol_cli *n); + +#endif diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c new file mode 100644 index 00000000..6b9112bf --- /dev/null +++ b/src/pulsecore/protocol-esound.c @@ -0,0 +1,1253 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <limits.h> + +#include <pulse/sample.h> +#include <pulse/timeval.h> +#include <pulse/utf8.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/esound.h> +#include <pulsecore/memblock.h> +#include <pulsecore/client.h> +#include <pulsecore/sink-input.h> +#include <pulsecore/sink.h> +#include <pulsecore/source-output.h> +#include <pulsecore/source.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/sample-util.h> +#include <pulsecore/authkey.h> +#include <pulsecore/namereg.h> +#include <pulsecore/log.h> +#include <pulsecore/core-util.h> +#include <pulsecore/core-error.h> + +#include "endianmacros.h" + +#include "protocol-esound.h" + +/* Don't accept more connection than this */ +#define MAX_CONNECTIONS 10 + +/* Kick a client if it doesn't authenticate within this time */ +#define AUTH_TIMEOUT 5 + +#define DEFAULT_COOKIE_FILE ".esd_auth" + +#define PLAYBACK_BUFFER_SECONDS (.25) +#define PLAYBACK_BUFFER_FRAGMENTS (10) +#define RECORD_BUFFER_SECONDS (5) +#define RECORD_BUFFER_FRAGMENTS (100) + +#define MAX_CACHE_SAMPLE_SIZE (1024000) + +#define SCACHE_PREFIX "esound." + +/* This is heavily based on esound's code */ + +struct connection { + uint32_t index; + int dead; + pa_protocol_esound *protocol; + pa_iochannel *io; + pa_client *client; + int authorized, swap_byte_order; + void *write_data; + size_t write_data_alloc, write_data_index, write_data_length; + void *read_data; + size_t read_data_alloc, read_data_length; + esd_proto_t request; + esd_client_state_t state; + pa_sink_input *sink_input; + pa_source_output *source_output; + pa_memblockq *input_memblockq, *output_memblockq; + pa_defer_event *defer_event; + + char *original_name; + + struct { + pa_memblock *current_memblock; + size_t memblock_index, fragment_size; + } playback; + + struct { + pa_memchunk memchunk; + char *name; + pa_sample_spec sample_spec; + } scache; + + pa_time_event *auth_timeout_event; +}; + +struct pa_protocol_esound { + int public; + pa_module *module; + pa_core *core; + pa_socket_server *server; + pa_idxset *connections; + char *sink_name, *source_name; + unsigned n_player; + uint8_t esd_key[ESD_KEY_LEN]; +}; + +typedef struct proto_handler { + size_t data_length; + int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length); + const char *description; +} esd_proto_handler_info_t; + +static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length); +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk); +static void sink_input_kill_cb(pa_sink_input *i); +static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i); +static pa_usec_t source_output_get_latency_cb(pa_source_output *o); + +static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk); +static void source_output_kill_cb(pa_source_output *o); + +static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_standby_or_resume(struct connection *c, esd_proto_t request, const void *data, size_t length); + +/* the big map of protocol handler info */ +static struct proto_handler proto_map[ESD_PROTO_MAX] = { + { ESD_KEY_LEN + sizeof(int), esd_proto_connect, "connect" }, + { ESD_KEY_LEN + sizeof(int), NULL, "lock" }, + { ESD_KEY_LEN + sizeof(int), NULL, "unlock" }, + + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" }, + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" }, + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" }, + + { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" }, /* 6 */ + { sizeof(int), esd_proto_sample_free_or_play, "sample free" }, + { sizeof(int), esd_proto_sample_free_or_play, "sample play" }, /* 8 */ + { sizeof(int), NULL, "sample loop" }, + { sizeof(int), NULL, "sample stop" }, + { -1, NULL, "TODO: sample kill" }, + + { ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "standby" }, /* NOOP! */ + { ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "resume" }, /* NOOP! */ /* 13 */ + + { ESD_NAME_MAX, esd_proto_sample_get_id, "sample getid" }, /* 14 */ + { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" }, + + { sizeof(int), esd_proto_server_info, "server info" }, + { sizeof(int), esd_proto_all_info, "all info" }, + { -1, NULL, "TODO: subscribe" }, + { -1, NULL, "TODO: unsubscribe" }, + + { 3 * sizeof(int), esd_proto_stream_pan, "stream pan"}, + { 3 * sizeof(int), NULL, "sample pan" }, + + { sizeof(int), NULL, "standby mode" }, + { 0, esd_proto_get_latency, "get latency" } +}; + +static void connection_free(struct connection *c) { + assert(c); + pa_idxset_remove_by_data(c->protocol->connections, c, NULL); + + if (c->state == ESD_STREAMING_DATA) + c->protocol->n_player--; + + pa_client_free(c->client); + + if (c->sink_input) { + pa_sink_input_disconnect(c->sink_input); + pa_sink_input_unref(c->sink_input); + } + + if (c->source_output) { + pa_source_output_disconnect(c->source_output); + pa_source_output_unref(c->source_output); + } + + if (c->input_memblockq) + pa_memblockq_free(c->input_memblockq); + if (c->output_memblockq) + pa_memblockq_free(c->output_memblockq); + + if (c->playback.current_memblock) + pa_memblock_unref(c->playback.current_memblock); + + pa_xfree(c->read_data); + pa_xfree(c->write_data); + + if (c->io) + pa_iochannel_free(c->io); + + if (c->defer_event) + c->protocol->core->mainloop->defer_free(c->defer_event); + + if (c->scache.memchunk.memblock) + pa_memblock_unref(c->scache.memchunk.memblock); + pa_xfree(c->scache.name); + + if (c->auth_timeout_event) + c->protocol->core->mainloop->time_free(c->auth_timeout_event); + + pa_xfree(c->original_name); + pa_xfree(c); +} + +static void connection_write_prepare(struct connection *c, size_t length) { + size_t t; + assert(c); + + t = c->write_data_length+length; + + if (c->write_data_alloc < t) + c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t); + + assert(c->write_data); +} + +static void connection_write(struct connection *c, const void *data, size_t length) { + size_t i; + assert(c); + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); + + connection_write_prepare(c, length); + + assert(c->write_data); + + i = c->write_data_length; + c->write_data_length += length; + + memcpy((char*)c->write_data + i, data, length); +} + +static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) { + assert(ss); + + ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; + if ((format & ESD_MASK_BITS) == ESD_BITS16) + ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE; + else + ss->format = PA_SAMPLE_U8; +} + +static int format_native2esd(pa_sample_spec *ss) { + int format = 0; + + format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16; + format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO; + + return format; +} + +#define CHECK_VALIDITY(expression, string) do { \ + if (!(expression)) { \ + pa_log_warn(__FILE__ ": " string); \ + return -1; \ + } \ +} while(0); + +/*** esound commands ***/ + +static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + uint32_t ekey; + int ok; + + assert(length == (ESD_KEY_LEN + sizeof(uint32_t))); + + if (!c->authorized) { + if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) { + pa_log(__FILE__": kicked client with invalid authorization key."); + return -1; + } + + c->authorized = 1; + if (c->auth_timeout_event) { + c->protocol->core->mainloop->time_free(c->auth_timeout_event); + c->auth_timeout_event = NULL; + } + } + + data = (const char*)data + ESD_KEY_LEN; + + memcpy(&ekey, data, sizeof(uint32_t)); + if (ekey == ESD_ENDIAN_KEY) + c->swap_byte_order = 0; + else if (ekey == ESD_SWAP_ENDIAN_KEY) + c->swap_byte_order = 1; + else { + pa_log(__FILE__": client sent invalid endian key"); + return -1; + } + + ok = 1; + connection_write(c, &ok, sizeof(int)); + return 0; +} + +static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + char name[ESD_NAME_MAX], *utf8_name; + int32_t format, rate; + pa_sink *sink; + pa_sample_spec ss; + size_t l; + + assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX)); + + memcpy(&format, data, sizeof(int32_t)); + format = MAYBE_INT32_SWAP(c->swap_byte_order, format); + data = (const char*)data + sizeof(int32_t); + + memcpy(&rate, data, sizeof(int32_t)); + rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); + data = (const char*)data + sizeof(int32_t); + + ss.rate = rate; + format_esd2native(format, c->swap_byte_order, &ss); + + CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification"); + sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1); + CHECK_VALIDITY(sink, "No such sink"); + + strncpy(name, data, sizeof(name)); + name[sizeof(name)-1] = 0; + utf8_name = pa_utf8_filter(name); + + pa_client_set_name(c->client, utf8_name); + c->original_name = pa_xstrdup(name); + + assert(!c->sink_input && !c->input_memblockq); + + c->sink_input = pa_sink_input_new(sink, __FILE__, utf8_name, &ss, NULL, NULL, 0, -1); + + pa_xfree(utf8_name); + + CHECK_VALIDITY(c->sink_input, "Failed to create sink input."); + + l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); + c->input_memblockq = pa_memblockq_new( + 0, + l, + 0, + pa_frame_size(&ss), + (size_t) -1, + l/PLAYBACK_BUFFER_FRAGMENTS, + NULL, + c->protocol->core->memblock_stat); + pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); + c->playback.fragment_size = l/10; + + c->sink_input->owner = c->protocol->module; + c->sink_input->client = c->client; + c->sink_input->peek = sink_input_peek_cb; + c->sink_input->drop = sink_input_drop_cb; + c->sink_input->kill = sink_input_kill_cb; + c->sink_input->get_latency = sink_input_get_latency_cb; + c->sink_input->userdata = c; + + c->state = ESD_STREAMING_DATA; + + c->protocol->n_player++; + + return 0; +} + +static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) { + char name[ESD_NAME_MAX], *utf8_name; + int32_t format, rate; + pa_source *source; + pa_sample_spec ss; + size_t l; + + assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX)); + + memcpy(&format, data, sizeof(int32_t)); + format = MAYBE_INT32_SWAP(c->swap_byte_order, format); + data = (const char*)data + sizeof(int32_t); + + memcpy(&rate, data, sizeof(int32_t)); + rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); + data = (const char*)data + sizeof(int32_t); + + ss.rate = rate; + format_esd2native(format, c->swap_byte_order, &ss); + + CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification."); + + if (request == ESD_PROTO_STREAM_MON) { + pa_sink* sink; + + if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": no such sink."); + return -1; + } + + if (!(source = sink->monitor_source)) { + pa_log(__FILE__": no such monitor source."); + return -1; + } + } else { + assert(request == ESD_PROTO_STREAM_REC); + + if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) { + pa_log(__FILE__": no such source."); + return -1; + } + } + + strncpy(name, data, sizeof(name)); + name[sizeof(name)-1] = 0; + + utf8_name = pa_utf8_filter(name); + pa_client_set_name(c->client, utf8_name); + pa_xfree(utf8_name); + + c->original_name = pa_xstrdup(name); + + assert(!c->output_memblockq && !c->source_output); + + if (!(c->source_output = pa_source_output_new(source, __FILE__, c->client->name, &ss, NULL, -1))) { + pa_log(__FILE__": failed to create source output"); + return -1; + } + + l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); + c->output_memblockq = pa_memblockq_new( + 0, + l, + 0, + pa_frame_size(&ss), + 1, + 0, + NULL, + c->protocol->core->memblock_stat); + pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2); + + c->source_output->owner = c->protocol->module; + c->source_output->client = c->client; + c->source_output->push = source_output_push_cb; + c->source_output->kill = source_output_kill_cb; + c->source_output->get_latency = source_output_get_latency_cb; + c->source_output->userdata = c; + + c->state = ESD_STREAMING_DATA; + + c->protocol->n_player++; + + return 0; +} + +static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + pa_sink *sink; + int32_t latency; + + assert(c && !data && length == 0); + + if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) + latency = 0; + else { + double usec = pa_sink_get_latency(sink); + latency = (int) ((usec*44100)/1000000); + } + + latency = MAYBE_INT32_SWAP(c->swap_byte_order, latency); + connection_write(c, &latency, sizeof(int32_t)); + return 0; +} + +static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16; + int32_t response; + pa_sink *sink; + + assert(c && data && length == sizeof(int32_t)); + + if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) { + rate = sink->sample_spec.rate; + format = format_native2esd(&sink->sample_spec); + } + + connection_write_prepare(c, sizeof(int32_t) * 3); + + response = 0; + connection_write(c, &response, sizeof(int32_t)); + rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); + connection_write(c, &rate, sizeof(int32_t)); + format = MAYBE_INT32_SWAP(c->swap_byte_order, format); + connection_write(c, &format, sizeof(int32_t)); + + return 0; +} + +static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) { + size_t t, k, s; + struct connection *conn; + uint32_t idx = PA_IDXSET_INVALID; + unsigned nsamples; + char terminator[sizeof(int32_t)*6+ESD_NAME_MAX]; + + assert(c && data && length == sizeof(int32_t)); + + if (esd_proto_server_info(c, request, data, length) < 0) + return -1; + + k = sizeof(int32_t)*5+ESD_NAME_MAX; + s = sizeof(int32_t)*6+ESD_NAME_MAX; + nsamples = c->protocol->core->scache ? pa_idxset_size(c->protocol->core->scache) : 0; + t = s*(nsamples+1) + k*(c->protocol->n_player+1); + + connection_write_prepare(c, t); + + memset(terminator, 0, sizeof(terminator)); + + for (conn = pa_idxset_first(c->protocol->connections, &idx); conn; conn = pa_idxset_next(c->protocol->connections, &idx)) { + int32_t id, format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE; + char name[ESD_NAME_MAX]; + + if (conn->state != ESD_STREAMING_DATA) + continue; + + assert(t >= k*2+s); + + if (conn->sink_input) { + pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input); + rate = conn->sink_input->sample_spec.rate; + lvolume = (volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM; + rvolume = (volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM; + format = format_native2esd(&conn->sink_input->sample_spec); + } + + /* id */ + id = MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1)); + connection_write(c, &id, sizeof(int32_t)); + + /* name */ + memset(name, 0, ESD_NAME_MAX); /* don't leak old data */ + if (conn->original_name) + strncpy(name, conn->original_name, ESD_NAME_MAX); + else if (conn->client && conn->client->name) + strncpy(name, conn->client->name, ESD_NAME_MAX); + connection_write(c, name, ESD_NAME_MAX); + + /* rate */ + rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); + connection_write(c, &rate, sizeof(int32_t)); + + /* left */ + lvolume = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume); + connection_write(c, &lvolume, sizeof(int32_t)); + + /*right*/ + rvolume = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume); + connection_write(c, &rvolume, sizeof(int32_t)); + + /*format*/ + format = MAYBE_INT32_SWAP(c->swap_byte_order, format); + connection_write(c, &format, sizeof(int32_t)); + + t -= k; + } + + assert(t == s*(nsamples+1)+k); + t -= k; + + connection_write(c, terminator, k); + + if (nsamples) { + pa_scache_entry *ce; + + idx = PA_IDXSET_INVALID; + for (ce = pa_idxset_first(c->protocol->core->scache, &idx); ce; ce = pa_idxset_next(c->protocol->core->scache, &idx)) { + int32_t id, rate, lvolume, rvolume, format, len; + char name[ESD_NAME_MAX]; + + assert(t >= s*2); + + /* id */ + id = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1)); + connection_write(c, &id, sizeof(int32_t)); + + /* name */ + memset(name, 0, ESD_NAME_MAX); /* don't leak old data */ + if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0) + strncpy(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX); + else + snprintf(name, ESD_NAME_MAX, "native.%s", ce->name); + connection_write(c, name, ESD_NAME_MAX); + + /* rate */ + rate = MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate); + connection_write(c, &rate, sizeof(int32_t)); + + /* left */ + lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); + connection_write(c, &lvolume, sizeof(int32_t)); + + /*right*/ + rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); + connection_write(c, &rvolume, sizeof(int32_t)); + + /*format*/ + format = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec)); + connection_write(c, &format, sizeof(int32_t)); + + /*length*/ + len = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length); + connection_write(c, &len, sizeof(int32_t)); + + t -= s; + } + } + + assert(t == s); + + connection_write(c, terminator, s); + + return 0; +} + +static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + int32_t ok; + uint32_t idx, lvolume, rvolume; + struct connection *conn; + + assert(c && data && length == sizeof(int32_t)*3); + + memcpy(&idx, data, sizeof(uint32_t)); + idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1; + data = (const char*)data + sizeof(uint32_t); + + memcpy(&lvolume, data, sizeof(uint32_t)); + lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume); + data = (const char*)data + sizeof(uint32_t); + + memcpy(&rvolume, data, sizeof(uint32_t)); + rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume); + data = (const char*)data + sizeof(uint32_t); + + if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) { + pa_cvolume volume; + volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; + volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; + volume.channels = 2; + pa_sink_input_set_volume(conn->sink_input, &volume); + ok = 1; + } else + ok = 0; + + connection_write(c, &ok, sizeof(int32_t)); + + return 0; +} + +static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + pa_sample_spec ss; + int32_t format, rate, sc_length; + uint32_t idx; + char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; + + assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int32_t))); + + memcpy(&format, data, sizeof(int32_t)); + format = MAYBE_INT32_SWAP(c->swap_byte_order, format); + data = (const char*)data + sizeof(int32_t); + + memcpy(&rate, data, sizeof(int32_t)); + rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate); + data = (const char*)data + sizeof(int32_t); + + ss.rate = rate; + format_esd2native(format, c->swap_byte_order, &ss); + + CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification."); + + memcpy(&sc_length, data, sizeof(int32_t)); + sc_length = MAYBE_INT32_SWAP(c->swap_byte_order, sc_length); + data = (const char*)data + sizeof(int32_t); + + CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large."); + + strcpy(name, SCACHE_PREFIX); + strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); + name[sizeof(name)-1] = 0; + + CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name."); + + assert(!c->scache.memchunk.memblock); + c->scache.memchunk.memblock = pa_memblock_new(sc_length, c->protocol->core->memblock_stat); + c->scache.memchunk.index = 0; + c->scache.memchunk.length = sc_length; + c->scache.sample_spec = ss; + assert(!c->scache.name); + c->scache.name = pa_xstrdup(name); + + c->state = ESD_CACHING_SAMPLE; + + pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx); + + idx += 1; + connection_write(c, &idx, sizeof(uint32_t)); + + return 0; +} + +static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + int32_t ok; + uint32_t idx; + char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; + + assert(c && data && length == ESD_NAME_MAX); + + strcpy(name, SCACHE_PREFIX); + strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); + name[sizeof(name)-1] = 0; + + CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name."); + + ok = -1; + if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID) + ok = idx + 1; + + connection_write(c, &ok, sizeof(int32_t)); + + return 0; +} + +static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) { + int32_t ok; + const char *name; + uint32_t idx; + + assert(c && data && length == sizeof(int32_t)); + + memcpy(&idx, data, sizeof(uint32_t)); + idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1; + + ok = 0; + + if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) { + if (request == ESD_PROTO_SAMPLE_PLAY) { + pa_sink *sink; + + if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) + if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0) + ok = idx + 1; + } else { + assert(request == ESD_PROTO_SAMPLE_FREE); + + if (pa_scache_remove_item(c->protocol->core, name) >= 0) + ok = idx + 1; + } + } + + connection_write(c, &ok, sizeof(int32_t)); + + return 0; +} + +static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) { + int32_t ok; + + connection_write_prepare(c, sizeof(int32_t) * 2); + + ok = 1; + connection_write(c, &ok, sizeof(int32_t)); + connection_write(c, &ok, sizeof(int32_t)); + + return 0; +} + +/*** client callbacks ***/ + +static void client_kill_cb(pa_client *c) { + assert(c && c->userdata); + connection_free(c->userdata); +} + +/*** pa_iochannel callbacks ***/ + +static int do_read(struct connection *c) { + assert(c && c->io); + +/* pa_log("READ"); */ + + if (c->state == ESD_NEXT_REQUEST) { + ssize_t r; + assert(c->read_data_length < sizeof(c->request)); + + if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) { + pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); + return -1; + } + + if ((c->read_data_length+= r) >= sizeof(c->request)) { + struct proto_handler *handler; + + c->request = MAYBE_INT32_SWAP(c->swap_byte_order, c->request); + + if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) { + pa_log(__FILE__": recieved invalid request."); + return -1; + } + + handler = proto_map+c->request; + +/* pa_log(__FILE__": executing request #%u", c->request); */ + + if (!handler->proc) { + pa_log(__FILE__": recieved unimplemented request #%u.", c->request); + return -1; + } + + if (handler->data_length == 0) { + c->read_data_length = 0; + + if (handler->proc(c, c->request, NULL, 0) < 0) + return -1; + + } else { + if (c->read_data_alloc < handler->data_length) + c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length); + assert(c->read_data); + + c->state = ESD_NEEDS_REQDATA; + c->read_data_length = 0; + } + } + + } else if (c->state == ESD_NEEDS_REQDATA) { + ssize_t r; + struct proto_handler *handler = proto_map+c->request; + + assert(handler->proc); + + assert(c->read_data && c->read_data_length < handler->data_length); + + if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) { + pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); + return -1; + } + + if ((c->read_data_length += r) >= handler->data_length) { + size_t l = c->read_data_length; + assert(handler->proc); + + c->state = ESD_NEXT_REQUEST; + c->read_data_length = 0; + + if (handler->proc(c, c->request, c->read_data, l) < 0) + return -1; + } + } else if (c->state == ESD_CACHING_SAMPLE) { + ssize_t r; + + assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length); + + if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) { + pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); + return -1; + } + + c->scache.memchunk.index += r; + assert(c->scache.memchunk.index <= c->scache.memchunk.length); + + if (c->scache.memchunk.index == c->scache.memchunk.length) { + uint32_t idx; + + c->scache.memchunk.index = 0; + pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx); + + pa_memblock_unref(c->scache.memchunk.memblock); + c->scache.memchunk.memblock = NULL; + c->scache.memchunk.index = c->scache.memchunk.length = 0; + + pa_xfree(c->scache.name); + c->scache.name = NULL; + + c->state = ESD_NEXT_REQUEST; + + idx += 1; + connection_write(c, &idx, sizeof(uint32_t)); + } + + } else if (c->state == ESD_STREAMING_DATA && c->sink_input) { + pa_memchunk chunk; + ssize_t r; + size_t l; + + assert(c->input_memblockq); + +/* pa_log("STREAMING_DATA"); */ + + if (!(l = pa_memblockq_missing(c->input_memblockq))) + return 0; + + if (l > c->playback.fragment_size) + l = c->playback.fragment_size; + + if (c->playback.current_memblock) + if (c->playback.current_memblock->length - c->playback.memblock_index < l) { + pa_memblock_unref(c->playback.current_memblock); + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + } + + if (!c->playback.current_memblock) { + c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat); + assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); + c->playback.memblock_index = 0; + } + + if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { + pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); + return -1; + } + + chunk.memblock = c->playback.current_memblock; + chunk.index = c->playback.memblock_index; + chunk.length = r; + assert(chunk.memblock); + + c->playback.memblock_index += r; + + assert(c->input_memblockq); + pa_memblockq_push_align(c->input_memblockq, &chunk); + assert(c->sink_input); + pa_sink_notify(c->sink_input->sink); + } + + return 0; +} + +static int do_write(struct connection *c) { + assert(c && c->io); + +/* pa_log("WRITE"); */ + + if (c->write_data_length) { + ssize_t r; + + assert(c->write_data_index < c->write_data_length); + if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) { + pa_log(__FILE__": write(): %s", pa_cstrerror(errno)); + return -1; + } + + if ((c->write_data_index +=r) >= c->write_data_length) + c->write_data_length = c->write_data_index = 0; + + } else if (c->state == ESD_STREAMING_DATA && c->source_output) { + pa_memchunk chunk; + ssize_t r; + + assert(c->output_memblockq); + if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) + return 0; + + assert(chunk.memblock && chunk.length); + + if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { + pa_memblock_unref(chunk.memblock); + pa_log(__FILE__": write(): %s", pa_cstrerror(errno)); + return -1; + } + + pa_memblockq_drop(c->output_memblockq, &chunk, r); + pa_memblock_unref(chunk.memblock); + + pa_source_notify(c->source_output->source); + } + + return 0; +} + +static void do_work(struct connection *c) { + assert(c); + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + c->protocol->core->mainloop->defer_enable(c->defer_event, 0); + + if (c->dead) + return; + + if (pa_iochannel_is_readable(c->io)) { + if (do_read(c) < 0) + goto fail; + } + + if (c->state == ESD_STREAMING_DATA && c->source_output && pa_iochannel_is_hungup(c->io)) + /* In case we are in capture mode we will never call read() + * on the socket, hence we need to detect the hangup manually + * here, instead of simply waiting for read() to return 0. */ + goto fail; + + if (pa_iochannel_is_writable(c->io)) + if (do_write(c) < 0) + goto fail; + + return; + +fail: + + if (c->state == ESD_STREAMING_DATA && c->sink_input) { + c->dead = 1; + + pa_iochannel_free(c->io); + c->io = NULL; + + pa_memblockq_prebuf_disable(c->input_memblockq); + pa_sink_notify(c->sink_input->sink); + } else + connection_free(c); +} + +static void io_callback(pa_iochannel*io, void *userdata) { + struct connection *c = userdata; + assert(io && c && c->io == io); + + do_work(c); +} + +/*** defer callback ***/ + +static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { + struct connection *c = userdata; + assert(a && c && c->defer_event == e); + +/* pa_log("DEFER"); */ + + do_work(c); +} + +/*** sink_input callbacks ***/ + +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { + struct connection*c; + assert(i && i->userdata && chunk); + c = i->userdata; + + if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) { + + if (c->dead) + connection_free(c); + + return -1; + } + + return 0; +} + +static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { + struct connection*c = i->userdata; + assert(i && c && length); + +/* pa_log("DROP"); */ + + pa_memblockq_drop(c->input_memblockq, chunk, length); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + + if (!c->dead) + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); + +/* assert(pa_memblockq_get_length(c->input_memblockq) > 2048); */ +} + +static void sink_input_kill_cb(pa_sink_input *i) { + assert(i && i->userdata); + connection_free((struct connection *) i->userdata); +} + +static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { + struct connection*c = i->userdata; + assert(i && c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); +} + +/*** source_output callbacks ***/ + +static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { + struct connection *c = o->userdata; + assert(o && c && chunk); + + pa_memblockq_push(c->output_memblockq, chunk); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + + if (!c->dead) + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); +} + +static void source_output_kill_cb(pa_source_output *o) { + assert(o && o->userdata); + connection_free((struct connection *) o->userdata); +} + +static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { + struct connection*c = o->userdata; + assert(o && c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec); +} + +/*** socket server callback ***/ + +static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { + struct connection *c = userdata; + assert(m && tv && c && c->auth_timeout_event == e); + + if (!c->authorized) + connection_free(c); +} + +static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { + struct connection *c; + pa_protocol_esound *p = userdata; + char cname[256], pname[128]; + assert(s && io && p); + + if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { + pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); + pa_iochannel_free(io); + return; + } + + c = pa_xnew(struct connection, 1); + c->protocol = p; + c->io = io; + pa_iochannel_set_callback(c->io, io_callback, c); + + pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); + snprintf(cname, sizeof(cname), "EsounD client (%s)", pname); + assert(p->core); + c->client = pa_client_new(p->core, __FILE__, cname); + assert(c->client); + c->client->owner = p->module; + c->client->kill = client_kill_cb; + c->client->userdata = c; + + c->authorized = p->public; + c->swap_byte_order = 0; + c->dead = 0; + + c->read_data_length = 0; + c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length); + + c->write_data_length = c->write_data_index = c->write_data_alloc = 0; + c->write_data = NULL; + + c->state = ESD_NEEDS_REQDATA; + c->request = ESD_PROTO_CONNECT; + + c->sink_input = NULL; + c->input_memblockq = NULL; + + c->source_output = NULL; + c->output_memblockq = NULL; + + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + c->playback.fragment_size = 0; + + c->scache.memchunk.length = c->scache.memchunk.index = 0; + c->scache.memchunk.memblock = NULL; + c->scache.name = NULL; + + c->original_name = NULL; + + if (!c->authorized) { + struct timeval tv; + pa_gettimeofday(&tv); + tv.tv_sec += AUTH_TIMEOUT; + c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c); + } else + c->auth_timeout_event = NULL; + + c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); + assert(c->defer_event); + p->core->mainloop->defer_enable(c->defer_event, 0); + + pa_idxset_put(p->connections, c, &c->index); +} + +/*** entry points ***/ + +pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { + pa_protocol_esound *p; + int public = 0; + assert(core && server && ma); + + p = pa_xnew(pa_protocol_esound, 1); + + if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) { + pa_log(__FILE__": auth-anonymous= expects a boolean argument."); + return NULL; + } + + if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) { + pa_xfree(p); + return NULL; + } + + p->module = m; + p->public = public; + p->server = server; + pa_socket_server_set_callback(p->server, on_connection, p); + p->core = core; + p->connections = pa_idxset_new(NULL, NULL); + assert(p->connections); + + p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); + p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL)); + p->n_player = 0; + + return p; +} + +void pa_protocol_esound_free(pa_protocol_esound *p) { + struct connection *c; + assert(p); + + while ((c = pa_idxset_first(p->connections, NULL))) + connection_free(c); + + pa_idxset_free(p->connections, NULL, NULL); + pa_socket_server_unref(p->server); + pa_xfree(p); +} diff --git a/src/pulsecore/protocol-esound.h b/src/pulsecore/protocol-esound.h new file mode 100644 index 00000000..79b5acf0 --- /dev/null +++ b/src/pulsecore/protocol-esound.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolesoundhfoo +#define fooprotocolesoundhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulsecore/core.h> +#include <pulsecore/socket-server.h> +#include <pulsecore/module.h> +#include <pulsecore/modargs.h> + +typedef struct pa_protocol_esound pa_protocol_esound; + +pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma); +void pa_protocol_esound_free(pa_protocol_esound *p); + +#endif diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c new file mode 100644 index 00000000..d0d92629 --- /dev/null +++ b/src/pulsecore/protocol-http.c @@ -0,0 +1,268 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif < |