summaryrefslogtreecommitdiff
path: root/src/libs/strings
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/strings')
-rw-r--r--src/libs/strings/Makeinfo2
-rw-r--r--src/libs/strings/getstr.c643
-rw-r--r--src/libs/strings/sfileins.c50
-rw-r--r--src/libs/strings/sresins.c55
-rw-r--r--src/libs/strings/stringhashtable.c67
-rw-r--r--src/libs/strings/stringhashtable.h43
-rw-r--r--src/libs/strings/strings.c347
-rw-r--r--src/libs/strings/strintrn.h56
-rw-r--r--src/libs/strings/unicode.c541
9 files changed, 1804 insertions, 0 deletions
diff --git a/src/libs/strings/Makeinfo b/src/libs/strings/Makeinfo
new file mode 100644
index 0000000..f1e4a9e
--- /dev/null
+++ b/src/libs/strings/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="getstr.c sfileins.c sresins.c stringhashtable.c strings.c unicode.c"
+uqm_HFILES="stringhashtable.c strintrn.h"
diff --git a/src/libs/strings/getstr.c b/src/libs/strings/getstr.c
new file mode 100644
index 0000000..ba428cf
--- /dev/null
+++ b/src/libs/strings/getstr.c
@@ -0,0 +1,643 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "options.h"
+#include "strintrn.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/reslib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+#define MAX_STRINGS 2048
+#define POOL_SIZE 4096
+
+static void
+dword_convert (DWORD *dword_array, COUNT num_dwords)
+{
+ BYTE *p = (BYTE*)dword_array;
+
+ do
+ {
+ *dword_array++ = MAKE_DWORD (
+ MAKE_WORD (p[3], p[2]),
+ MAKE_WORD (p[1], p[0])
+ );
+ p += 4;
+ } while (--num_dwords);
+}
+
+static STRING
+set_strtab_entry (STRING_TABLE_DESC *strtab, int index, const char *value,
+ int len)
+{
+ STRING str = &strtab->strings[index];
+
+ if (str->data)
+ {
+ HFree (str->data);
+ str->data = NULL;
+ str->length = 0;
+ }
+ if (len)
+ {
+ str->data = HMalloc (len);
+ str->length = len;
+ memcpy (str->data, value, len);
+ }
+ return str;
+}
+
+static void
+copy_strings_to_strtab (STRING_TABLE_DESC *strtab, size_t firstIndex,
+ size_t count, const char *data, const DWORD *lens)
+{
+ size_t stringI;
+ const char *off = data;
+
+ for (stringI = 0; stringI < count; stringI++)
+ {
+ set_strtab_entry(strtab, firstIndex + stringI,
+ off, lens[stringI]);
+ off += lens[stringI];
+ }
+}
+
+// Check whether a buffer has a certain minimum size, and enlarge it
+// if necessary.
+// buf: pointer to the pointer to the buffer. May be NULL.
+// curSize: pointer to the current size (multiple of 'increment')
+// minSize: required minimum size
+// increment: size to increment the buffer with if necessary
+// On success, *buf and *curSize are updated. On failure, they are
+// unchanged.
+// returns FALSE if and only if the buffer needs to be enlarged but
+// memory allocation failed.
+static BOOLEAN
+ensureBufSize (char **buf, size_t *curSize, size_t minSize, size_t increment)
+{
+ char *newBuf;
+ size_t newSize;
+
+ if (minSize <= *curSize)
+ {
+ // Buffer is large enough as it is.
+ return TRUE;
+ }
+
+ newSize = ((minSize + (increment - 1)) / increment) * increment;
+ // Smallest multiple of 'increment' larger or equal to minSize.
+ newBuf = HRealloc (*buf, newSize);
+ if (newBuf == NULL)
+ return FALSE;
+
+ // Success
+ *buf = newBuf;
+ *curSize = newSize;
+ return TRUE;
+}
+
+void
+_GetConversationData (const char *path, RESOURCE_DATA *resdata)
+{
+ unsigned long dataLen;
+ void *result;
+ int stringI;
+ int path_len;
+ int num_data_sets;
+ DWORD opos;
+
+ char *namedata = NULL;
+ // Contains the names (indexes) of the dialogs.
+ DWORD nlen[MAX_STRINGS];
+ // Length of each of the names.
+ DWORD NameOffs;
+ size_t tot_name_size;
+
+ char *strdata = NULL;
+ // Contains the dialog strings.
+ DWORD slen[MAX_STRINGS];
+ // Length of each of the dialog strings.
+ DWORD StringOffs;
+ size_t tot_string_size;
+
+ char *clipdata = NULL;
+ // Contains the file names of the speech files.
+ DWORD clen[MAX_STRINGS];
+ // Length of each of the speech file names.
+ DWORD ClipOffs;
+ size_t tot_clip_size;
+
+ char *ts_data = NULL;
+ // Contains the timestamp data for synching the text with the
+ // speech.
+ DWORD tslen[MAX_STRINGS];
+ // Length of each of the timestamp strings.
+ DWORD TSOffs;
+ size_t tot_ts_size = 0;
+
+ char CurrentLine[1024];
+ char paths[1024];
+ char *clip_path;
+ char *ts_path;
+
+ uio_Stream *fp = NULL;
+ uio_Stream *timestamp_fp = NULL;
+ StringHashTable_HashTable *nameHashTable = NULL;
+ // Hash table of string names (such as "GLAD_WHEN_YOU_COME_BACK")
+ // to a STRING.
+
+ /* Parse out the conversation components. */
+ strncpy (paths, path, 1023);
+ paths[1023] = '\0';
+ clip_path = strchr (paths, ':');
+ if (clip_path == NULL)
+ {
+ ts_path = NULL;
+ }
+ else
+ {
+ *clip_path = '\0';
+ clip_path++;
+
+ ts_path = strchr (clip_path, ':');
+ if (ts_path != NULL)
+ {
+ *ts_path = '\0';
+ ts_path++;
+ }
+ }
+
+ fp = res_OpenResFile (contentDir, paths, "rb");
+ if (fp == NULL)
+ {
+ log_add (log_Warning, "Warning: Can't open '%s'", paths);
+ resdata->ptr = NULL;
+ return;
+ }
+
+ dataLen = LengthResFile (fp);
+ log_add (log_Info, "\t'%s' -- conversation phrases -- %lu bytes", paths,
+ dataLen);
+ if (clip_path)
+ log_add (log_Info, "\t'%s' -- voice clip directory", clip_path);
+ else
+ log_add (log_Info, "\tNo associated voice clips");
+ if (ts_path)
+ log_add (log_Info, "\t'%s' -- timestamps", ts_path);
+ else
+ log_add (log_Info, "\tNo associated timestamp file");
+
+ if (dataLen == 0)
+ {
+ log_add (log_Warning, "Warning: Trying to load empty file '%s'.",
+ path);
+ goto err;
+ }
+
+ tot_string_size = POOL_SIZE;
+ strdata = HMalloc (tot_string_size);
+ if (strdata == 0)
+ goto err;
+
+ tot_name_size = POOL_SIZE;
+ namedata = HMalloc (tot_name_size);
+ if (namedata == 0)
+ goto err;
+
+ tot_clip_size = POOL_SIZE;
+ clipdata = HMalloc (tot_clip_size);
+ if (clipdata == 0)
+ goto err;
+ ts_data = NULL;
+
+ nameHashTable = StringHashTable_newHashTable(
+ NULL, NULL, NULL, NULL, NULL, 0, 0.85, 0.9);
+ if (nameHashTable == NULL)
+ goto err;
+
+ path_len = clip_path ? strlen (clip_path) : 0;
+
+ if (ts_path)
+ {
+ timestamp_fp = uio_fopen (contentDir, ts_path, "rb");
+ if (timestamp_fp != NULL)
+ {
+ tot_ts_size = POOL_SIZE;
+ ts_data = HMalloc (tot_ts_size);
+ if (ts_data == 0)
+ goto err;
+ }
+ }
+
+ opos = uio_ftell (fp);
+ stringI = -1;
+ NameOffs = 0;
+ StringOffs = 0;
+ ClipOffs = 0;
+ TSOffs = 0;
+ for (;;)
+ {
+ int l;
+
+ if (uio_fgets (CurrentLine, sizeof (CurrentLine), fp) == NULL)
+ {
+ // EOF or read error.
+ break;
+ }
+
+ if (stringI >= MAX_STRINGS - 1)
+ {
+ // Too many strings.
+ break;
+ }
+
+ if (CurrentLine[0] == '#')
+ {
+ // String header, of the following form:
+ // #(GLAD_WHEN_YOU_COME_BACK) commander-000.ogg
+ char CopyLine[1024];
+ char *name;
+ char *ts;
+
+ strcpy (CopyLine, CurrentLine);
+ name = strtok (&CopyLine[1], "()");
+ if (name)
+ {
+ if (stringI >= 0)
+ {
+ while (slen[stringI] > 1 &&
+ (strdata[StringOffs - 2] == '\n' ||
+ strdata[StringOffs - 2] == '\r'))
+ {
+ --slen[stringI];
+ --StringOffs;
+ strdata[StringOffs - 1] = '\0';
+ }
+ }
+
+ slen[++stringI] = 0;
+
+ // Store the string name.
+ l = strlen (name) + 1;
+ if (!ensureBufSize (&namedata, &tot_name_size,
+ NameOffs + l, POOL_SIZE))
+ goto err;
+ strcpy (&namedata[NameOffs], name);
+ NameOffs += l;
+ nlen[stringI] = l;
+
+ // now lets check for timestamp data
+ if (timestamp_fp)
+ {
+ // We have a time stamp file.
+ char TimeStampLine[1024];
+ char *tsptr;
+ BOOLEAN ts_ok = FALSE;
+ uio_fgets (TimeStampLine, sizeof (TimeStampLine), timestamp_fp);
+ if (TimeStampLine[0] == '#')
+ {
+ // Line is of the following form:
+ // #(GIVE_FUEL_AGAIN) 3304,3255
+ tslen[stringI] = 0;
+ tsptr = strstr (TimeStampLine, name);
+ if (tsptr)
+ {
+ tsptr += strlen(name) + 1;
+ ts_ok = TRUE;
+ while (! strcspn(tsptr," \t\r\n") && *tsptr)
+ tsptr++;
+ if (*tsptr)
+ {
+ l = strlen (tsptr) + 1;
+ if (!ensureBufSize (&ts_data, &tot_ts_size, TSOffs + l,
+ POOL_SIZE))
+ goto err;
+
+ strcpy (&ts_data[TSOffs], tsptr);
+ TSOffs += l;
+ tslen[stringI] = l;
+ }
+ }
+ }
+ if (!ts_ok)
+ {
+ // timestamp data is invalid, remove all of it
+ log_add (log_Warning, "Invalid timestamp data "
+ "for '%s'. Disabling timestamps", name);
+ HFree (ts_data);
+ ts_data = NULL;
+ uio_fclose (timestamp_fp);
+ timestamp_fp = NULL;
+ TSOffs = 0;
+ }
+ }
+ clen[stringI] = 0;
+ ts = strtok (NULL, " \t\r\n)");
+ if (ts)
+ {
+ l = path_len + strlen (ts) + 1;
+ if (!ensureBufSize (&clipdata, &tot_clip_size,
+ ClipOffs + l, POOL_SIZE))
+ goto err;
+
+ if (clip_path)
+ strcpy (&clipdata[ClipOffs], clip_path);
+ strcpy (&clipdata[ClipOffs + path_len], ts);
+ ClipOffs += l;
+ clen[stringI] = l;
+ }
+ }
+ }
+ else if (stringI >= 0)
+ {
+ char *s;
+ l = strlen (CurrentLine) + 1;
+
+ if (!ensureBufSize (&strdata, &tot_string_size, StringOffs + l,
+ POOL_SIZE))
+ goto err;
+
+ if (slen[stringI])
+ {
+ --slen[stringI];
+ --StringOffs;
+ }
+ s = &strdata[StringOffs];
+ slen[stringI] += l;
+ StringOffs += l;
+
+ strcpy (s, CurrentLine);
+ }
+
+ if ((int)uio_ftell (fp) - (int)opos >= (int)dataLen)
+ break;
+ }
+ if (stringI >= 0)
+ {
+ while (slen[stringI] > 1 && (strdata[StringOffs - 2] == '\n'
+ || strdata[StringOffs - 2] == '\r'))
+ {
+ --slen[stringI];
+ --StringOffs;
+ strdata[StringOffs - 1] = '\0';
+ }
+ }
+
+ if (timestamp_fp)
+ uio_fclose (timestamp_fp);
+
+ result = NULL;
+ num_data_sets = (ClipOffs ? 1 : 0) + (TSOffs ? 1 : 0) + 1;
+ if (++stringI)
+ {
+ int flags = 0;
+ int stringCount = stringI;
+
+ if (ClipOffs)
+ flags |= HAS_SOUND_CLIPS;
+ if (TSOffs)
+ flags |= HAS_TIMESTAMP;
+ flags |= HAS_NAMEINDEX;
+
+ result = AllocStringTable (stringCount, flags);
+ if (result)
+ {
+ // Copy all the gatherered data in a STRING_TABLE
+ STRING_TABLE_DESC *lpST = (STRING_TABLE) result;
+ STRING str;
+ stringI = 0;
+
+ // Store the dialog string.
+ copy_strings_to_strtab (
+ lpST, stringI, stringCount, strdata, slen);
+ stringI += stringCount;
+
+ // Store the dialog names.
+ copy_strings_to_strtab (
+ lpST, stringI, stringCount, namedata, nlen);
+ stringI += stringCount;
+
+ // Store sound clip file names.
+ if (lpST->flags & HAS_SOUND_CLIPS)
+ {
+ copy_strings_to_strtab (
+ lpST, stringI, stringCount, clipdata, clen);
+ stringI += stringCount;
+ }
+
+ // Store time stamp data.
+ if (lpST->flags & HAS_TIMESTAMP)
+ {
+ copy_strings_to_strtab (
+ lpST, stringI, stringCount, ts_data, tslen);
+ //stringI += stringCount;
+ }
+
+ // Store the STRING in the hash table indexed by the dialog
+ // name.
+ str = &lpST->strings[stringCount];
+ for (stringI = 0; stringI < stringCount; stringI++)
+ {
+ StringHashTable_add (nameHashTable, str[stringI].data,
+ &str[stringI]);
+ }
+
+ lpST->nameIndex = nameHashTable;
+ }
+ }
+ HFree (strdata);
+ if (clipdata != NULL)
+ HFree (clipdata);
+ if (ts_data != NULL)
+ HFree (ts_data);
+
+ resdata->ptr = result;
+ return;
+
+err:
+ if (nameHashTable != NULL)
+ StringHashTable_deleteHashTable (nameHashTable);
+ if (ts_data != NULL)
+ HFree (ts_data);
+ if (clipdata != NULL)
+ HFree (clipdata);
+ if (strdata != NULL)
+ HFree (strdata);
+ res_CloseResFile (fp);
+ resdata->ptr = NULL;
+}
+
+void *
+_GetStringData (uio_Stream *fp, DWORD length)
+{
+ void *result;
+
+ int stringI;
+ DWORD opos;
+ DWORD slen[MAX_STRINGS];
+ DWORD StringOffs;
+ size_t tot_string_size;
+ char CurrentLine[1024];
+ char *strdata = NULL;
+
+ tot_string_size = POOL_SIZE;
+ strdata = HMalloc (tot_string_size);
+ if (strdata == 0)
+ goto err;
+
+ opos = uio_ftell (fp);
+ stringI = -1;
+ StringOffs = 0;
+ for (;;)
+ {
+ int l;
+
+ if (uio_fgets (CurrentLine, sizeof (CurrentLine), fp) == NULL)
+ {
+ // EOF or read error.
+ break;
+ }
+
+ if (stringI >= MAX_STRINGS - 1)
+ {
+ // Too many strings.
+ break;
+ }
+
+ if (CurrentLine[0] == '#')
+ {
+ char CopyLine[1024];
+ char *s;
+
+ strcpy (CopyLine, CurrentLine);
+ s = strtok (&CopyLine[1], "()");
+ if (s)
+ {
+ if (stringI >= 0)
+ {
+ while (slen[stringI] > 1 &&
+ (strdata[StringOffs - 2] == '\n' ||
+ strdata[StringOffs - 2] == '\r'))
+ {
+ --slen[stringI];
+ --StringOffs;
+ strdata[StringOffs - 1] = '\0';
+ }
+ }
+
+ slen[++stringI] = 0;
+ }
+ }
+ else if (stringI >= 0)
+ {
+ char *s;
+ l = strlen (CurrentLine) + 1;
+
+ if (!ensureBufSize (&strdata, &tot_string_size, StringOffs + l,
+ POOL_SIZE))
+ goto err;
+
+ if (slen[stringI])
+ {
+ --slen[stringI];
+ --StringOffs;
+ }
+ s = &strdata[StringOffs];
+ slen[stringI] += l;
+ StringOffs += l;
+
+ strcpy (s, CurrentLine);
+ }
+
+ if ((int)uio_ftell (fp) - (int)opos >= (int)length)
+ break;
+ }
+ if (stringI >= 0)
+ {
+ while (slen[stringI] > 1 && (strdata[StringOffs - 2] == '\n'
+ || strdata[StringOffs - 2] == '\r'))
+ {
+ --slen[stringI];
+ --StringOffs;
+ strdata[StringOffs - 1] = '\0';
+ }
+ }
+
+ result = NULL;
+ if (++stringI)
+ {
+ int flags = 0;
+ int stringCount = stringI;
+
+ result = AllocStringTable (stringI, flags);
+ if (result)
+ {
+ STRING_TABLE_DESC *lpST = (STRING_TABLE) result;
+ copy_strings_to_strtab (lpST, 0, stringCount, strdata, slen);
+ }
+ }
+ HFree (strdata);
+
+ return result;
+
+err:
+ if (strdata != NULL)
+ HFree (strdata);
+ return 0;
+}
+
+
+void *
+_GetBinaryTableData (uio_Stream *fp, DWORD length)
+{
+ void *result;
+ result = GetResourceData (fp, length);
+
+ if (result)
+ {
+ DWORD *fileData;
+ STRING_TABLE lpST;
+
+ fileData = (DWORD *)result;
+
+ dword_convert (fileData, 1); /* Length */
+
+ lpST = AllocStringTable (fileData[0], 0);
+ if (lpST)
+ {
+ int i, size;
+ BYTE *stringptr;
+
+ size = lpST->size;
+
+ dword_convert (fileData+1, size + 1);
+ stringptr = (BYTE *)(fileData + 2 + size + fileData[1]);
+ for (i = 0; i < size; i++)
+ {
+ set_strtab_entry (lpST, i, (char *)stringptr, fileData[2+i]);
+ stringptr += fileData[2+i];
+ }
+ }
+ HFree (result);
+ result = lpST;
+ }
+
+ return result;
+}
+
diff --git a/src/libs/strings/sfileins.c b/src/libs/strings/sfileins.c
new file mode 100644
index 0000000..6ff4422
--- /dev/null
+++ b/src/libs/strings/sfileins.c
@@ -0,0 +1,50 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "strintrn.h"
+#include "libs/uio.h"
+#include "libs/reslib.h"
+
+
+STRING_TABLE
+LoadStringTableFile (uio_DirHandle *dir, const char *fileName)
+{
+ uio_Stream *fp;
+
+ // FIXME: this theoretically needs a mechanism to prevent races
+ if (_cur_resfile_name)
+ // something else is loading resources atm
+ return 0;
+
+ fp = res_OpenResFile (dir, fileName, "rb");
+ if (fp)
+ {
+ STRING_TABLE data;
+
+ _cur_resfile_name = fileName;
+ data = (STRING_TABLE) _GetStringData (fp, LengthResFile (fp));
+ _cur_resfile_name = 0;
+ res_CloseResFile (fp);
+
+ return data;
+ }
+
+ return (0);
+}
+
diff --git a/src/libs/strings/sresins.c b/src/libs/strings/sresins.c
new file mode 100644
index 0000000..af2de79
--- /dev/null
+++ b/src/libs/strings/sresins.c
@@ -0,0 +1,55 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "strintrn.h"
+
+static void
+GetStringTableFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetStringData);
+}
+
+static void
+GetBinaryTableFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetBinaryTableData);
+}
+
+BOOLEAN
+InstallStringTableResType (void)
+{
+ InstallResTypeVectors ("STRTAB", GetStringTableFileData, FreeResourceData, NULL);
+ InstallResTypeVectors ("BINTAB", GetBinaryTableFileData, FreeResourceData, NULL);
+ InstallResTypeVectors ("CONVERSATION", _GetConversationData, FreeResourceData, NULL);
+ return TRUE;
+}
+
+STRING_TABLE
+LoadStringTableInstance (RESOURCE res)
+{
+ void *data;
+
+ data = res_GetResource (res);
+ if (data)
+ {
+ res_DetachResource (res);
+ }
+
+ return (STRING_TABLE)data;
+}
+
diff --git a/src/libs/strings/stringhashtable.c b/src/libs/strings/stringhashtable.c
new file mode 100644
index 0000000..ac4b4f4
--- /dev/null
+++ b/src/libs/strings/stringhashtable.c
@@ -0,0 +1,67 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define HASHTABLE_INTERNAL
+#include "stringhashtable.h"
+#include "types.h"
+#include "libs/misc.h"
+ // For unconst()
+#include "libs/uio/uioport.h"
+
+static inline uio_uint32 StringHashTable_hash(
+ StringHashTable_HashTable *hashTable, const char *string);
+static inline uio_bool StringHashTable_equal(
+ StringHashTable_HashTable *hashTable,
+ const char *key1, const char *key2);
+static inline char *StringHashTable_copy(
+ StringHashTable_HashTable *hashTable, const char *key);
+
+#include "libs/uio/hashtable.c"
+
+
+static inline uio_uint32
+StringHashTable_hash(StringHashTable_HashTable *hashTable, const char *key) {
+ uio_uint32 hash;
+
+ (void) hashTable;
+ // Rotating hash, variation of something on the web which
+ // wasn't original itself.
+ hash = 0;
+ // Hash was on that web page initialised as the length,
+ // but that isn't known at this time.
+ while (*key != '\0') {
+ hash = (hash << 4) ^ (hash >> 28) ^ *key;
+ key++;
+ }
+ return hash ^ (hash >> 10) ^ (hash >> 20);
+}
+
+static inline uio_bool
+StringHashTable_equal(StringHashTable_HashTable *hashTable,
+ const char *key1, const char *key2) {
+ (void) hashTable;
+ return strcmp(key1, key2) == 0;
+}
+
+static inline char *
+StringHashTable_copy(StringHashTable_HashTable *hashTable,
+ const char *key) {
+ (void) hashTable;
+ return unconst(key);
+}
+
diff --git a/src/libs/strings/stringhashtable.h b/src/libs/strings/stringhashtable.h
new file mode 100644
index 0000000..36f9e47
--- /dev/null
+++ b/src/libs/strings/stringhashtable.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _STRINGHASHTABLE_H
+#define _STRINGHASHTABLE_H
+
+// HashTable from 'char *' to STRING.
+// We don't actually copy the index, which means that the caller is
+// responsible for keeping them unchanged during the time that it is used in
+// the hash table.
+
+#include "libs/strlib.h"
+
+#define HASHTABLE_(identifier) StringHashTable ## _ ## identifier
+typedef char HASHTABLE_(Key);
+typedef STRING_TABLE_ENTRY_DESC HASHTABLE_(Value);
+#define StringHashTable_HASH StringHashTable_hash
+#define StringHashTable_EQUAL StringHashTable_equal
+#define StringHashTable_COPY StringHashTable_copy
+#define StringHashTable_FREEKEY(hashTable, key) \
+ ((void) (hashTable), (void) (key))
+#define StringHashTable_FREEVALUE(hashTable, value) \
+ ((void) (hashTable), (void) (value))
+
+#include "libs/uio/hashtable.h"
+
+
+#endif /* _STRINGHASHTABLE_H */
diff --git a/src/libs/strings/strings.c b/src/libs/strings/strings.c
new file mode 100644
index 0000000..7f8d5e4
--- /dev/null
+++ b/src/libs/strings/strings.c
@@ -0,0 +1,347 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "strintrn.h"
+#include "libs/memlib.h"
+
+STRING_TABLE
+AllocStringTable (int num_entries, int flags)
+{
+ STRING_TABLE strtab = HMalloc (sizeof (STRING_TABLE_DESC));
+ int i, multiplier = 1;
+
+ if (flags & HAS_NAMEINDEX)
+ {
+ multiplier++;
+ }
+ if (flags & HAS_SOUND_CLIPS)
+ {
+ multiplier++;
+ }
+ if (flags & HAS_TIMESTAMP)
+ {
+ multiplier++;
+ }
+ strtab->flags = flags;
+ strtab->size = num_entries;
+ num_entries *= multiplier;
+ strtab->strings = HMalloc (sizeof (STRING_TABLE_ENTRY_DESC) * num_entries);
+ for (i = 0; i < num_entries; i++)
+ {
+ strtab->strings[i].data = NULL;
+ strtab->strings[i].length = 0;
+ strtab->strings[i].parent = strtab;
+ strtab->strings[i].index = i;
+ }
+ strtab->nameIndex = NULL;
+ return strtab;
+}
+
+void
+FreeStringTable (STRING_TABLE strtab)
+{
+ int i, multiplier = 1;
+
+ if (strtab == NULL)
+ {
+ return;
+ }
+
+ if (strtab->flags & HAS_SOUND_CLIPS)
+ {
+ multiplier++;
+ }
+ if (strtab->flags & HAS_TIMESTAMP)
+ {
+ multiplier++;
+ }
+
+ for (i = 0; i < strtab->size * multiplier; i++)
+ {
+ if (strtab->strings[i].data != NULL)
+ {
+ HFree (strtab->strings[i].data);
+ }
+ }
+
+ HFree (strtab->strings);
+ HFree (strtab);
+}
+
+BOOLEAN
+DestroyStringTable (STRING_TABLE StringTable)
+{
+ FreeStringTable (StringTable);
+ return TRUE;
+}
+
+STRING
+CaptureStringTable (STRING_TABLE StringTable)
+{
+ if ((StringTable != 0) && (StringTable->size > 0))
+ {
+ return StringTable->strings;
+ }
+
+ return NULL;
+}
+
+STRING_TABLE
+ReleaseStringTable (STRING String)
+{
+ STRING_TABLE StringTable;
+
+ StringTable = GetStringTable (String);
+
+ return (StringTable);
+}
+
+STRING_TABLE
+GetStringTable (STRING String)
+{
+ if (String && String->parent)
+ {
+ return String->parent;
+ }
+ return NULL;
+}
+
+COUNT
+GetStringTableCount (STRING String)
+{
+ if (String && String->parent)
+ {
+ return String->parent->size;
+ }
+ return 0;
+}
+
+COUNT
+GetStringTableIndex (STRING String)
+{
+ if (String)
+ {
+ return String->index;
+ }
+ return 0;
+}
+
+STRING
+SetAbsStringTableIndex (STRING String, COUNT StringTableIndex)
+{
+ STRING_TABLE StringTablePtr;
+
+ if (!String)
+ return NULL;
+
+ StringTablePtr = String->parent;
+
+ if (StringTablePtr == NULL)
+ {
+ String = NULL;
+ }
+ else
+ {
+ StringTableIndex = StringTableIndex % StringTablePtr->size;
+ String = &StringTablePtr->strings[StringTableIndex];
+ }
+
+ return (String);
+}
+
+STRING
+SetRelStringTableIndex (STRING String, SIZE StringTableOffs)
+{
+ STRING_TABLE StringTablePtr;
+
+ if (!String)
+ return NULL;
+
+ StringTablePtr = String->parent;
+
+ if (StringTablePtr == NULL)
+ {
+ String = NULL;
+ }
+ else
+ {
+ COUNT StringTableIndex;
+
+ while (StringTableOffs < 0)
+ StringTableOffs += StringTablePtr->size;
+ StringTableIndex = (String->index + StringTableOffs)
+ % StringTablePtr->size;
+
+ String = &StringTablePtr->strings[StringTableIndex];
+ }
+
+ return (String);
+}
+
+COUNT
+GetStringLength (STRING String)
+{
+ if (String == NULL)
+ {
+ return 0;
+ }
+ return utf8StringCountN(String->data, String->data + String->length);
+}
+
+COUNT
+GetStringLengthBin (STRING String)
+{
+ if (String == NULL)
+ {
+ return 0;
+ }
+ return String->length;
+}
+
+STRINGPTR
+GetStringName (STRING String)
+{
+ STRING_TABLE StringTablePtr;
+ COUNT StringIndex;
+
+ if (String == NULL)
+ {
+ return NULL;
+ }
+
+ StringTablePtr = String->parent;
+ if (StringTablePtr == NULL)
+ {
+ return NULL;
+ }
+
+ StringIndex = String->index;
+
+ if (!(StringTablePtr->flags & HAS_NAMEINDEX))
+ {
+ return NULL;
+ }
+ StringIndex += StringTablePtr->size;
+
+ String = &StringTablePtr->strings[StringIndex];
+ if (String->length == 0)
+ {
+ return NULL;
+ }
+
+ return String->data;
+}
+
+STRINGPTR
+GetStringSoundClip (STRING String)
+{
+ STRING_TABLE StringTablePtr;
+ COUNT StringIndex;
+
+ if (String == NULL)
+ {
+ return NULL;
+ }
+
+ StringTablePtr = String->parent;
+ if (StringTablePtr == NULL)
+ {
+ return NULL;
+ }
+
+ StringIndex = String->index;
+ if (!(StringTablePtr->flags & HAS_SOUND_CLIPS))
+ {
+ return NULL;
+ }
+ StringIndex += StringTablePtr->size;
+
+ if (StringTablePtr->flags & HAS_NAMEINDEX)
+ {
+ StringIndex += StringTablePtr->size;
+ }
+
+ String = &StringTablePtr->strings[StringIndex];
+ if (String->length == 0)
+ {
+ return NULL;
+ }
+
+ return String->data;
+}
+
+STRINGPTR
+GetStringTimeStamp (STRING String)
+{
+ STRING_TABLE StringTablePtr;
+ COUNT StringIndex;
+
+ if (String == NULL)
+ {
+ return NULL;
+ }
+
+ StringTablePtr = String->parent;
+ if (StringTablePtr == NULL)
+ {
+ return NULL;
+ }
+
+ StringIndex = String->index;
+ if (!(StringTablePtr->flags & HAS_TIMESTAMP))
+ {
+ return NULL;
+ }
+ StringIndex += StringTablePtr->size;
+
+ if (StringTablePtr->flags & HAS_NAMEINDEX)
+ {
+ StringIndex += StringTablePtr->size;
+ }
+
+ if (StringTablePtr->flags & HAS_SOUND_CLIPS)
+ {
+ StringIndex += StringTablePtr->size;
+ }
+
+ String = &StringTablePtr->strings[StringIndex];
+ if (String->length == 0)
+ {
+ return NULL;
+ }
+
+ return String->data;
+}
+
+STRINGPTR
+GetStringAddress (STRING String)
+{
+ if (String == NULL)
+ {
+ return NULL;
+ }
+ return String->data;
+}
+
+STRING
+GetStringByName (STRING_TABLE StringTable, const char *index)
+{
+ return (STRING) StringHashTable_find (StringTable->nameIndex, index);
+}
+
+
diff --git a/src/libs/strings/strintrn.h b/src/libs/strings/strintrn.h
new file mode 100644
index 0000000..0c41fb0
--- /dev/null
+++ b/src/libs/strings/strintrn.h
@@ -0,0 +1,56 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_STRINGS_STRINTRN_H_
+#define LIBS_STRINGS_STRINTRN_H_
+
+#include <stdio.h>
+#include <string.h>
+#include "libs/strlib.h"
+#include "libs/reslib.h"
+#include "stringhashtable.h"
+
+struct string_table_entry
+{
+ STRINGPTR data;
+ int length; /* Internal NULs are allowed */
+ int index;
+ struct string_table *parent;
+};
+
+struct string_table
+{
+ unsigned short flags;
+ int size;
+ STRING_TABLE_ENTRY_DESC *strings;
+ StringHashTable_HashTable *nameIndex;
+};
+
+#define HAS_SOUND_CLIPS (1 << 0)
+#define HAS_TIMESTAMP (1 << 1)
+#define HAS_NAMEINDEX (1 << 2)
+
+STRING_TABLE AllocStringTable (int num_entries, int flags);
+void FreeStringTable (STRING_TABLE strtab);
+
+void *_GetStringData (uio_Stream *fp, DWORD length);
+void *_GetBinaryTableData (uio_Stream *fp, DWORD length);
+void _GetConversationData (const char *path, RESOURCE_DATA *resdata);
+
+#endif /* LIBS_STRINGS_STRINTRN_H_ */
+
diff --git a/src/libs/strings/unicode.c b/src/libs/strings/unicode.c
new file mode 100644
index 0000000..1750507
--- /dev/null
+++ b/src/libs/strings/unicode.c
@@ -0,0 +1,541 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "port.h"
+
+#define UNICODE_INTERNAL
+#include "libs/unicode.h"
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs/log.h"
+#include "libs/misc.h"
+
+
+// Resynchronise (skip everything starting with 0x10xxxxxx):
+static inline void
+resyncUTF8(const unsigned char **ptr) {
+ while ((**ptr & 0xc0) == 0x80)
+ (*ptr)++;
+}
+
+// Get one character from a UTF-8 encoded string.
+// *ptr will point to the start of the next character.
+// Returns 0 if the encoding is bad. This can be distinguished from the
+// '\0' character by checking whether **ptr == '\0' before calling this
+// function.
+UniChar
+getCharFromString(const unsigned char **ptr) {
+ UniChar result;
+
+ if (**ptr < 0x80) {
+ // 0xxxxxxx, regular ASCII
+ result = **ptr;
+ (*ptr)++;
+
+ return result;
+ }
+
+ if ((**ptr & 0xe0) == 0xc0) {
+ // 110xxxxx; 10xxxxxx must follow
+ // Value between 0x00000080 and 0x000007ff (inclusive)
+ result = **ptr & 0x1f;
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if (result < 0x00000080) {
+ // invalid encoding - must reject
+ goto err;
+ }
+ return result;
+ }
+
+ if ((**ptr & 0xf0) == 0xe0) {
+ // 1110xxxx; 10xxxxxx 10xxxxxx must follow
+ // Value between 0x00000800 and 0x0000ffff (inclusive)
+ result = **ptr & 0x0f;
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if (result < 0x00000800) {
+ // invalid encoding - must reject
+ goto err;
+ }
+ return result;
+ }
+
+ if ((**ptr & 0xf8) == 0xf0) {
+ // 11110xxx; 10xxxxxx 10xxxxxx 10xxxxxx must follow
+ // Value between 0x00010000 and 0x0010ffff (inclusive)
+ result = **ptr & 0x07;
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if (result < 0x00010000) {
+ // invalid encoding - must reject
+ goto err;
+ }
+ return result;
+ }
+
+err:
+ log_add(log_Warning, "Warning: Invalid UTF8 sequence.");
+
+ // Resynchronise (skip everything starting with 0x10xxxxxx):
+ resyncUTF8(ptr);
+
+ return 0;
+}
+
+UniChar
+getCharFromStringN(const unsigned char **ptr, const unsigned char *end) {
+ size_t numBytes;
+
+ if (*ptr == end)
+ goto err;
+
+ if (**ptr < 0x80) {
+ numBytes = 1;
+ } else if ((**ptr & 0xe0) == 0xc0) {
+ numBytes = 2;
+ } else if ((**ptr & 0xf0) == 0xe0) {
+ numBytes = 3;
+ } else if ((**ptr & 0xf8) == 0xf0) {
+ numBytes = 4;
+ } else
+ goto err;
+
+ if (*ptr + numBytes > end)
+ goto err;
+
+ return getCharFromString(ptr);
+
+err:
+ *ptr = end;
+ return 0;
+}
+
+// Get one line from a string.
+// A line is terminated with either CRLF (DOS/Windows),
+// LF (Unix, MacOS X), or CR (old MacOS).
+// The end of the string is reached when **startNext == '\0'.
+// NULL is returned if the string is not valid UTF8. In this case
+// *end points to the first invalid character (or the character before if
+// it was a LF), and *startNext to the start of the next (possibly invalid
+// too) character.
+unsigned char *
+getLineFromString(const unsigned char *start, const unsigned char **end,
+ const unsigned char **startNext) {
+ const unsigned char *ptr = start;
+ const unsigned char *lastPtr;
+ UniChar ch;
+
+ // Search for the first newline.
+ for (;;) {
+ if (*ptr == '\0') {
+ *end = ptr;
+ *startNext = ptr;
+ return (unsigned char *) unconst(start);
+ }
+ lastPtr = ptr;
+ ch = getCharFromString(&ptr);
+ if (ch == '\0') {
+ // Bad string
+ *end = lastPtr;
+ *startNext = ptr;
+ return NULL;
+ }
+ if (ch == '\n') {
+ *end = lastPtr;
+ if (*ptr == '\0'){
+ // LF at the end of the string.
+ *startNext = ptr;
+ return (unsigned char *) unconst(start);
+ }
+ ch = getCharFromString(&ptr);
+ if (ch == '\0') {
+ // Bad string
+ return NULL;
+ }
+ if (ch == '\r') {
+ // LFCR
+ *startNext = ptr;
+ } else {
+ // LF
+ *startNext = *end;
+ }
+ return (unsigned char *) unconst(start);
+ } else if (ch == '\r') {
+ *end = lastPtr;
+ *startNext = ptr;
+ return (unsigned char *) unconst(start);
+ } // else: a normal character
+ }
+}
+
+size_t
+utf8StringCount(const unsigned char *start) {
+ size_t count = 0;
+ UniChar ch;
+
+ for (;;) {
+ ch = getCharFromString(&start);
+ if (ch == '\0')
+ return count;
+ count++;
+ }
+}
+
+size_t
+utf8StringCountN(const unsigned char *start, const unsigned char *end) {
+ size_t count = 0;
+ UniChar ch;
+
+ for (;;) {
+ ch = getCharFromStringN(&start, end);
+ if (ch == '\0')
+ return count;
+ count++;
+ }
+}
+
+// Locates a unicode character (ch) in a UTF-8 string (pStr)
+// returns the char positions when found
+// -1 when not found
+int
+utf8StringPos (const unsigned char *pStr, UniChar ch)
+{
+ int pos;
+
+ for (pos = 0; *pStr != '\0'; ++pos)
+ {
+ if (getCharFromString (&pStr) == ch)
+ return pos;
+ }
+
+ if (ch == '\0' && *pStr == '\0')
+ return pos;
+
+ return -1;
+}
+
+// Safe version of strcpy(), somewhat analogous to strncpy()
+// except it guarantees a 0-term when size > 0
+// when size == 0, returns NULL
+// BUG: this may result in the last character being only partially in the
+// buffer
+unsigned char *
+utf8StringCopy (unsigned char *dst, size_t size, const unsigned char *src)
+{
+ if (size == 0)
+ return 0;
+
+ strncpy ((char *) dst, (const char *) src, size);
+ dst[size - 1] = '\0';
+
+ return dst;
+}
+
+// TODO: this is not implemented with respect to collating order
+int
+utf8StringCompare (const unsigned char *str1, const unsigned char *str2)
+{
+#if 0
+ // UniChar comparing version
+ UniChar ch1;
+ UniChar ch2;
+
+ for (;;)
+ {
+ int cmp;
+
+ ch1 = getCharFromString(&str1);
+ ch2 = getCharFromString(&str2);
+ if (ch1 == '\0' || ch2 == '\0')
+ break;
+
+ cmp = utf8CompareChar (ch1, ch2);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ if (ch1 != '\0')
+ {
+ // ch2 == '\0'
+ // str2 ends, str1 continues
+ return 1;
+ }
+
+ if (ch2 != '\0')
+ {
+ // ch1 == '\0'
+ // str1 ends, str2 continues
+ return -1;
+ }
+
+ // ch1 == '\0' && ch2 == '\0'.
+ // Strings match completely.
+ return 0;
+#else
+ // this will do for now
+ return strcmp ((const char *) str1, (const char *) str2);
+#endif
+}
+
+unsigned char *
+skipUTF8Chars(const unsigned char *ptr, size_t num) {
+ UniChar ch;
+ const unsigned char *oldPtr;
+
+ while (num--) {
+ oldPtr = ptr;
+ ch = getCharFromString(&ptr);
+ if (ch == '\0')
+ return (unsigned char *) unconst(oldPtr);
+ }
+ return (unsigned char *) unconst(ptr);
+}
+
+// Decodes a UTF-8 string (start) into a unicode character string (wstr)
+// returns number of chars decoded and stored, not counting 0-term
+// any chars that do not fit are truncated
+// wide string term 0 is always appended, unless the destination
+// buffer is 0 chars long
+size_t
+getUniCharFromStringN(UniChar *wstr, size_t maxcount,
+ const unsigned char *start, const unsigned char *end)
+{
+ UniChar *next;
+
+ if (maxcount == 0)
+ return 0;
+
+ // always leave room for 0-term
+ --maxcount;
+
+ for (next = wstr; maxcount > 0; ++next, --maxcount)
+ {
+ *next = getCharFromStringN(&start, end);
+ if (*next == 0)
+ break;
+ }
+
+ *next = 0; // term
+
+ return next - wstr;
+}
+
+// See getStringFromWideN() for functionality
+// the only difference is that the source string (start) length is
+// calculated by searching for 0-term
+size_t
+getUniCharFromString(UniChar *wstr, size_t maxcount,
+ const unsigned char *start)
+{
+ UniChar *next;
+
+ if (maxcount == 0)
+ return 0;
+
+ // always leave room for 0-term
+ --maxcount;
+
+ for (next = wstr; maxcount > 0; ++next, --maxcount)
+ {
+ *next = getCharFromString(&start);
+ if (*next == 0)
+ break;
+ }
+
+ *next = 0; // term
+
+ return next - wstr;
+}
+
+// Encode one wide character into UTF-8
+// returns number of bytes used in the buffer,
+// 0 : invalid or unsupported char
+// <0 : negative of bytes needed if buffer too small
+// string term '\0' is *not* appended or counted
+int
+getStringFromChar(unsigned char *ptr, size_t size, UniChar ch)
+{
+ int i;
+ static const struct range_def
+ {
+ UniChar lim;
+ int marker;
+ int mask;
+ }
+ ranges[] =
+ {
+ {0x0000007f, 0x00, 0x7f},
+ {0x000007ff, 0xc0, 0x1f},
+ {0x0000ffff, 0xe0, 0x0f},
+ {0x001fffff, 0xf0, 0x07},
+ {0x03ffffff, 0xf8, 0x03},
+ {0x7fffffff, 0xfc, 0x01},
+ {0x00000000, 0x00, 0x00} // term
+ };
+ const struct range_def *def;
+
+ // lookup the range
+ for (i = 0, def = ranges; ch > def->lim && def->mask != 0; ++i, ++def)
+ ;
+ if (def->mask == 0)
+ { // invalid or unsupported char
+ log_add(log_Warning, "Warning: Invalid or unsupported unicode "
+ "char (%lu)", (unsigned long) ch);
+ return 0;
+ }
+
+ if ((size_t)i + 1 > size)
+ return -(i + 1);
+
+ // unrolled for speed
+ switch (i)
+ {
+ case 5: ptr[5] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 4: ptr[4] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 3: ptr[3] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 2: ptr[2] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 1: ptr[1] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 0: ptr[0] = (ch & def->mask) | def->marker;
+ }
+
+ return i + 1;
+}
+
+// Encode a wide char string (wstr) into a UTF-8 string (ptr)
+// returns number of bytes used in the buffer (includes 0-term)
+// any chars that do not fit are truncated
+// string term '\0' is always appended, unless the destination
+// buffer is 0 bytes long
+size_t
+getStringFromWideN(unsigned char *ptr, size_t size,
+ const UniChar *wstr, size_t count)
+{
+ unsigned char *next;
+ int used;
+
+ if (size == 0)
+ return 0;
+
+ // always leave room for 0-term
+ --size;
+
+ for (next = ptr; size > 0 && count > 0;
+ size -= used, next += used, --count, ++wstr)
+ {
+ used = getStringFromChar(next, size, *wstr);
+ if (used < 0)
+ break; // not enough room
+ if (used == 0)
+ { // bad char?
+ *next = '?';
+ used = 1;
+ }
+ }
+
+ *next = '\0'; // term
+
+ return next - ptr + 1;
+}
+
+// See getStringFromWideN() for functionality
+// the only difference is that the source string (wstr) length is
+// calculated by searching for 0-term
+size_t
+getStringFromWide(unsigned char *ptr, size_t size, const UniChar *wstr)
+{
+ const UniChar *end;
+
+ for (end = wstr; *end != 0; ++end)
+ ;
+
+ return getStringFromWideN(ptr, size, wstr, (end - wstr));
+}
+
+int
+UniChar_isGraph(UniChar ch)
+{ // this is not technically sufficient, but close enough for us
+ // we'll consider all non-control (CO and C1) chars in 'graph' class
+ // except for the "Private Use Area" (0xE000 - 0xF8FF)
+
+ // TODO: The private use area is really only glommed by OS X,
+ // and even there, not all of it. (Delete and Backspace both
+ // end up producing characters there -- see bug #942 for the
+ // gory details.)
+ return (ch > 0xa0 && (ch < 0xE000 || ch > 0xF8FF)) ||
+ (ch > 0x20 && ch < 0x7f);
+}
+
+int
+UniChar_isPrint(UniChar ch)
+{ // this is not technically sufficient, but close enough for us
+ // chars in 'print' class are 'graph' + 'space' classes
+ // the only space we currently have defined is 0x20
+ return (ch == 0x20) || UniChar_isGraph(ch);
+}
+
+UniChar
+UniChar_toUpper(UniChar ch)
+{ // this is a very basic Latin-1 implementation
+ // just to get things going
+ return (ch < 0x100) ? (UniChar) toupper((int) ch) : ch;
+}
+
+UniChar
+UniChar_toLower(UniChar ch)
+{ // this is a very basic Latin-1 implementation
+ // just to get things going
+ return (ch < 0x100) ? (UniChar) tolower((int) ch) : ch;
+}
+