diff options
Diffstat (limited to 'src/libs/strings')
-rw-r--r-- | src/libs/strings/Makeinfo | 2 | ||||
-rw-r--r-- | src/libs/strings/getstr.c | 643 | ||||
-rw-r--r-- | src/libs/strings/sfileins.c | 50 | ||||
-rw-r--r-- | src/libs/strings/sresins.c | 55 | ||||
-rw-r--r-- | src/libs/strings/stringhashtable.c | 67 | ||||
-rw-r--r-- | src/libs/strings/stringhashtable.h | 43 | ||||
-rw-r--r-- | src/libs/strings/strings.c | 347 | ||||
-rw-r--r-- | src/libs/strings/strintrn.h | 56 | ||||
-rw-r--r-- | src/libs/strings/unicode.c | 541 |
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; +} + |