//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; }