summaryrefslogtreecommitdiff
path: root/src/libs/resource
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/resource')
-rw-r--r--src/libs/resource/Makeinfo3
-rw-r--r--src/libs/resource/direct.c101
-rw-r--r--src/libs/resource/filecntl.c146
-rw-r--r--src/libs/resource/getres.c257
-rw-r--r--src/libs/resource/index.h54
-rw-r--r--src/libs/resource/loadres.c54
-rw-r--r--src/libs/resource/propfile.c129
-rw-r--r--src/libs/resource/propfile.h30
-rw-r--r--src/libs/resource/resinit.c651
-rw-r--r--src/libs/resource/resintrn.h34
-rw-r--r--src/libs/resource/stringbank.c181
-rw-r--r--src/libs/resource/stringbank.h57
12 files changed, 1697 insertions, 0 deletions
diff --git a/src/libs/resource/Makeinfo b/src/libs/resource/Makeinfo
new file mode 100644
index 0000000..ddac8e2
--- /dev/null
+++ b/src/libs/resource/Makeinfo
@@ -0,0 +1,3 @@
+uqm_CFILES="direct.c filecntl.c getres.c loadres.c stringbank.c
+ propfile.c resinit.c"
+uqm_HFILES="index.h propfile.h resintrn.h stringbank.h"
diff --git a/src/libs/resource/direct.c b/src/libs/resource/direct.c
new file mode 100644
index 0000000..b3d3541
--- /dev/null
+++ b/src/libs/resource/direct.c
@@ -0,0 +1,101 @@
+//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 "libs/strings/strintrn.h"
+#include "libs/memlib.h"
+#include "port.h"
+#include "libs/uio.h"
+#include <sys/stat.h>
+
+DIRENTRY_REF
+LoadDirEntryTable (uio_DirHandle *dirHandle, const char *path,
+ const char *pattern, match_MatchType matchType)
+{
+ uio_DirList *dirList;
+ COUNT num_entries;
+ COUNT i;
+ uio_DirHandle *dir;
+ STRING_TABLE StringTable;
+ STRING_TABLE_DESC *lpST;
+ STRING lpLastString;
+
+ dir = uio_openDirRelative (dirHandle, path, 0);
+ assert(dir != NULL);
+ dirList = uio_getDirList (dir, "", pattern, matchType);
+ assert(dirList != NULL);
+ num_entries = 0;
+
+ // First, count the amount of space needed
+ for (i = 0; i < dirList->numNames; i++)
+ {
+ struct stat sb;
+
+ if (dirList->names[i][0] == '.')
+ {
+ dirList->names[i] = NULL;
+ continue;
+ }
+ if (uio_stat (dir, dirList->names[i], &sb) == -1)
+ {
+ dirList->names[i] = NULL;
+ continue;
+ }
+ if (!S_ISREG (sb.st_mode))
+ {
+ dirList->names[i] = NULL;
+ continue;
+ }
+ num_entries++;
+ }
+ uio_closeDir (dir);
+
+ if (num_entries == 0) {
+ uio_DirList_free(dirList);
+ return ((DIRENTRY_REF) 0);
+ }
+
+ StringTable = AllocStringTable (num_entries, 0);
+ lpST = StringTable;
+ if (lpST == 0)
+ {
+ FreeStringTable (StringTable);
+ uio_DirList_free(dirList);
+ return ((DIRENTRY_REF) 0);
+ }
+ lpST->size = num_entries;
+ lpLastString = lpST->strings;
+
+ for (i = 0; i < dirList->numNames; i++)
+ {
+ int size;
+ STRINGPTR target;
+ if (dirList->names[i] == NULL)
+ continue;
+ size = strlen (dirList->names[i]) + 1;
+ target = HMalloc (size);
+ memcpy (target, dirList->names[i], size);
+ lpLastString->data = target;
+ lpLastString->length = size;
+ lpLastString++;
+ }
+
+ uio_DirList_free(dirList);
+ return StringTable;
+}
+
+
diff --git a/src/libs/resource/filecntl.c b/src/libs/resource/filecntl.c
new file mode 100644
index 0000000..e2a81d9
--- /dev/null
+++ b/src/libs/resource/filecntl.c
@@ -0,0 +1,146 @@
+//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.
+ */
+
+#ifdef WIN32
+#include <io.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "port.h"
+#include "resintrn.h"
+#include "libs/uio.h"
+
+uio_Stream *
+res_OpenResFile (uio_DirHandle *dir, const char *filename, const char *mode)
+{
+ uio_Stream *fp;
+ struct stat sb;
+
+ if (uio_stat (dir, filename, &sb) == 0 && S_ISDIR(sb.st_mode))
+ return ((uio_Stream *) ~0);
+
+ fp = uio_fopen (dir, filename, mode);
+
+ return (fp);
+}
+
+BOOLEAN
+res_CloseResFile (uio_Stream *fp)
+{
+ if (fp)
+ {
+ if (fp != (uio_Stream *)~0)
+ uio_fclose (fp);
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+BOOLEAN
+DeleteResFile (uio_DirHandle *dir, const char *filename)
+{
+ return (uio_unlink (dir, filename) == 0);
+}
+
+size_t
+ReadResFile (void *lpBuf, size_t size, size_t count, uio_Stream *fp)
+{
+ int retval;
+
+ retval = uio_fread (lpBuf, size, count, fp);
+
+ return (retval);
+}
+
+size_t
+WriteResFile (const void *lpBuf, size_t size, size_t count, uio_Stream *fp)
+{
+ int retval;
+
+ retval = uio_fwrite (lpBuf, size, count, fp);
+
+ return (retval);
+}
+
+int
+GetResFileChar (uio_Stream *fp)
+{
+ int retval;
+
+ retval = uio_getc (fp);
+
+ return (retval);
+}
+
+int
+PutResFileChar (char ch, uio_Stream *fp)
+{
+ int retval;
+
+ retval = uio_putc (ch, fp);
+ return (retval);
+}
+
+int
+PutResFileNewline (uio_Stream *fp)
+{
+ int retval;
+
+#ifdef WIN32
+ PutResFileChar ('\r', fp);
+#endif
+ retval = PutResFileChar ('\n', fp);
+ return (retval);
+}
+
+long
+SeekResFile (uio_Stream *fp, long offset, int whence)
+{
+ long retval;
+
+ retval = uio_fseek (fp, offset, whence);
+
+ return (retval);
+}
+
+long
+TellResFile (uio_Stream *fp)
+{
+ long retval;
+
+ retval = uio_ftell (fp);
+
+ return (retval);
+}
+
+size_t
+LengthResFile (uio_Stream *fp)
+{
+ struct stat sb;
+
+ if (fp == (uio_Stream *)~0)
+ return (1);
+ if (uio_fstat(uio_streamHandle(fp), &sb) == -1)
+ return 1;
+ return sb.st_size;
+}
+
+
diff --git a/src/libs/resource/getres.c b/src/libs/resource/getres.c
new file mode 100644
index 0000000..39e24a9
--- /dev/null
+++ b/src/libs/resource/getres.c
@@ -0,0 +1,257 @@
+//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 "port.h"
+#include "resintrn.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+#include "libs/uio/charhashtable.h"
+
+const char *_cur_resfile_name;
+// When a file is being loaded, _cur_resfile_name is set to its name.
+// At other times, it is NULL.
+
+ResourceDesc *
+lookupResourceDesc (RESOURCE_INDEX idx, RESOURCE res)
+{
+ return (ResourceDesc *) CharHashTable_find (idx->map, res);
+}
+
+void
+loadResourceDesc (ResourceDesc *desc)
+{
+ desc->vtable->loadFun (desc->fname, &desc->resdata);
+}
+
+void *
+LoadResourceFromPath (const char *path, ResourceLoadFileFun *loadFun)
+{
+ uio_Stream *stream;
+ unsigned long dataLen;
+ void *resdata;
+
+ stream = res_OpenResFile (contentDir, path, "rb");
+ if (stream == NULL)
+ {
+ log_add (log_Warning, "Warning: Can't open '%s'", path);
+ return NULL;
+ }
+
+ dataLen = LengthResFile (stream);
+ log_add (log_Info, "\t'%s' -- %lu bytes", path, dataLen);
+
+ if (dataLen == 0)
+ {
+ log_add (log_Warning, "Warning: Trying to load empty file '%s'.", path);
+ goto err;
+ }
+
+ _cur_resfile_name = path;
+ resdata = (*loadFun) (stream, dataLen);
+ _cur_resfile_name = NULL;
+ res_CloseResFile (stream);
+
+ return resdata;
+
+err:
+ res_CloseResFile (stream);
+ return NULL;
+}
+
+const char *
+res_GetResourceType (RESOURCE res)
+{
+ RESOURCE_INDEX resourceIndex;
+ ResourceDesc *desc;
+
+ if (res == NULL_RESOURCE)
+ {
+ log_add (log_Warning, "Trying to get type of null resource");
+ return NULL;
+ }
+
+ resourceIndex = _get_current_index_header ();
+ desc = lookupResourceDesc (resourceIndex, res);
+ if (desc == NULL)
+ {
+ log_add (log_Warning, "Trying to get type of undefined resource '%s'",
+ res);
+ return NULL;
+ }
+
+ return desc->vtable->resType;
+}
+
+
+// Get a resource by its resource ID.
+void *
+res_GetResource (RESOURCE res)
+{
+ RESOURCE_INDEX resourceIndex;
+ ResourceDesc *desc;
+
+ if (res == NULL_RESOURCE)
+ {
+ log_add (log_Warning, "Trying to get null resource");
+ return NULL;
+ }
+
+ resourceIndex = _get_current_index_header ();
+
+ desc = lookupResourceDesc (resourceIndex, res);
+ if (desc == NULL)
+ {
+ log_add (log_Warning, "Trying to get undefined resource '%s'",
+ res);
+ return NULL;
+ }
+
+ if (desc->resdata.ptr == NULL)
+ loadResourceDesc (desc);
+ if (desc->resdata.ptr != NULL)
+ ++desc->refcount;
+
+ return desc->resdata.ptr;
+ // May still be NULL, if the load failed.
+}
+
+DWORD
+res_GetIntResource (RESOURCE res)
+{
+ RESOURCE_INDEX resourceIndex;
+ ResourceDesc *desc;
+
+ if (res == NULL_RESOURCE)
+ {
+ log_add (log_Warning, "Trying to get null resource");
+ return 0;
+ }
+
+ resourceIndex = _get_current_index_header ();
+
+ desc = lookupResourceDesc (resourceIndex, res);
+ if (desc == NULL)
+ {
+ log_add (log_Warning, "Trying to get undefined resource '%s'",
+ res);
+ return 0;
+ }
+
+ return desc->resdata.num;
+}
+
+BOOLEAN
+res_GetBooleanResource (RESOURCE res)
+{
+ return (res_GetIntResource (res) != 0);
+}
+
+// NB: this function appears to be never called!
+void
+res_FreeResource (RESOURCE res)
+{
+ ResourceDesc *desc;
+ ResourceFreeFun *freeFun;
+
+ desc = lookupResourceDesc (_get_current_index_header(), res);
+ if (desc == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to free an unrecognised "
+ "resource.");
+ return;
+ }
+
+ if (desc->refcount > 0)
+ --desc->refcount;
+ else
+ log_add (log_Debug, "Warning: freeing an unreferenced resource.");
+ if (desc->refcount > 0)
+ return; // Still references left
+
+ freeFun = desc->vtable->freeFun;
+ if (freeFun == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to free a non-heap resource.");
+ return;
+ }
+
+ if (desc->resdata.ptr == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to free not loaded "
+ "resource.");
+ return;
+ }
+
+ (*freeFun) (desc->resdata.ptr);
+ desc->resdata.ptr = NULL;
+}
+
+// By calling this function the caller will be responsible of unloading
+// the resource. If res_GetResource() get called again for this
+// resource, a NEW copy will be loaded, regardless of whether a detached
+// copy still exists.
+void *
+res_DetachResource (RESOURCE res)
+{
+ ResourceDesc *desc;
+ ResourceFreeFun *freeFun;
+ void *result;
+
+ desc = lookupResourceDesc (_get_current_index_header(), res);
+ if (desc == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to detach from an unrecognised "
+ "resource.");
+ return NULL;
+ }
+
+ freeFun = desc->vtable->freeFun;
+ if (freeFun == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to detach from a non-heap resource.");
+ return NULL;
+ }
+
+ if (desc->resdata.ptr == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to detach from a not loaded "
+ "resource.");
+ return NULL;
+ }
+
+ if (desc->refcount > 1)
+ {
+ log_add (log_Debug, "Warning: trying to detach a resource referenced "
+ "%u times", desc->refcount);
+ return NULL;
+ }
+
+ result = desc->resdata.ptr;
+ desc->resdata.ptr = NULL;
+ desc->refcount = 0;
+
+ return result;
+}
+
+BOOLEAN
+FreeResourceData (void *data)
+{
+ HFree (data);
+ return TRUE;
+}
diff --git a/src/libs/resource/index.h b/src/libs/resource/index.h
new file mode 100644
index 0000000..bdbb162
--- /dev/null
+++ b/src/libs/resource/index.h
@@ -0,0 +1,54 @@
+//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_RESOURCE_INDEX_H_
+#define LIBS_RESOURCE_INDEX_H_
+
+typedef struct resource_handlers ResourceHandlers;
+typedef struct resource_desc ResourceDesc;
+
+#include <stdio.h>
+#include "libs/reslib.h"
+#include "libs/uio/charhashtable.h"
+
+struct resource_handlers
+{
+ const char *resType;
+ ResourceLoadFun *loadFun;
+ ResourceFreeFun *freeFun;
+ ResourceStringFun *toString;
+};
+
+struct resource_desc
+{
+ RESOURCE res_id;
+ char *fname;
+ ResourceHandlers *vtable;
+ RESOURCE_DATA resdata;
+ // refcount is rudimentary as nothing really frees the descriptors
+ unsigned refcount;
+};
+
+struct resource_index_desc
+{
+ CharHashTable_HashTable *map;
+ size_t numRes;
+};
+
+#endif /* LIBS_RESOURCE_INDEX_H_ */
+
diff --git a/src/libs/resource/loadres.c b/src/libs/resource/loadres.c
new file mode 100644
index 0000000..a9849e4
--- /dev/null
+++ b/src/libs/resource/loadres.c
@@ -0,0 +1,54 @@
+//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 "resintrn.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+
+
+void *
+GetResourceData (uio_Stream *fp, DWORD length)
+{
+ void *result;
+ DWORD compLen;
+
+ // Resource data used to be prefixed by its length in package files.
+ // A valid length prefix indicated compressed data, and
+ // a length prefix ~0 meant uncompressed.
+ // Currently, .ct and .xlt files still carry a ~0 length prefix.
+ if (ReadResFile (&compLen, sizeof (compLen), 1, fp) != 1)
+ return NULL;
+ if (compLen != ~(DWORD)0)
+ {
+ log_add (log_Warning, "LZ-compressed binary data not supported");
+ return NULL;
+ }
+ length -= sizeof (DWORD);
+
+ result = AllocResourceData (length);
+ if (!result)
+ return NULL;
+
+ if (ReadResFile (result, 1, length, fp) != length)
+ {
+ FreeResourceData (result);
+ result = NULL;
+ }
+
+ return result;
+}
diff --git a/src/libs/resource/propfile.c b/src/libs/resource/propfile.c
new file mode 100644
index 0000000..1784600
--- /dev/null
+++ b/src/libs/resource/propfile.c
@@ -0,0 +1,129 @@
+/* propfile.c, Copyright (c) 2008 Michael C. Martin */
+
+/*
+ * 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 thta it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Se 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "libs/log.h"
+#include "propfile.h"
+#include "libs/reslib.h"
+
+void
+PropFile_from_string (char *d, PROPERTY_HANDLER handler, const char *prefix)
+{
+ int len, i;
+
+ len = strlen(d);
+ i = 0;
+ while (i < len) {
+ int key_start, key_end, value_start, value_end;
+ /* Starting a line: search for non-whitespace */
+ while ((i < len) && isspace (d[i])) i++;
+ if (i >= len) break; /* Done parsing! */
+ /* If it was a comment, skip to end of comment/file */
+ if (d[i] == '#') {
+ while ((i < len) && (d[i] != '\n')) i++;
+ if (i >= len) break;
+ continue; /* Back to keyword search */
+ }
+ key_start = i;
+ /* Find the = on this line */
+ while ((i < len) && (d[i] != '=') &&
+ (d[i] != '\n') && (d[i] != '#')) i++;
+ if (i >= len) { /* Bare key at EOF */
+ log_add (log_Warning, "Warning: Bare keyword at EOF");
+ break;
+ }
+ /* Comments here mean incomplete line too */
+ if (d[i] != '=') {
+ log_add (log_Warning, "Warning: Key without value");
+ while ((i < len) && (d[i] != '\n')) i++;
+ if (i >= len) break;
+ continue; /* Back to keyword search */
+ }
+ /* Key ends at first whitespace before = , or at key_start*/
+ key_end = i;
+ while ((key_end > key_start) && isspace (d[key_end-1]))
+ key_end--;
+
+ /* Consume the = */
+ i++;
+ /* Value starts at first non-whitespace after = on line... */
+ while ((i < len) && (d[i] != '#') && (d[i] != '\n') &&
+ isspace (d[i]))
+ i++;
+ value_start = i;
+ /* Until first non-whitespace before terminator */
+ while ((i < len) && (d[i] != '#') && (d[i] != '\n'))
+ i++;
+ value_end = i;
+ while ((value_end > value_start) && isspace (d[value_end-1]))
+ value_end--;
+ /* Skip past EOL or EOF */
+ while ((i < len) && (d[i] != '\n'))
+ i++;
+ i++;
+
+ /* We now have start and end values for key and value.
+ We terminate the strings for both by writing \0s, then
+ make a new map entry. */
+ d[key_end] = '\0';
+ d[value_end] = '\0';
+ if (prefix) {
+ char buf[256];
+ snprintf(buf, 255, "%s%s", prefix, d+key_start);
+ buf[255]=0;
+ handler(buf, d+value_start);
+ } else {
+ handler (d+key_start, d+value_start);
+ }
+ }
+}
+
+void
+PropFile_from_file (uio_Stream *f, PROPERTY_HANDLER handler, const char *prefix)
+{
+ size_t flen;
+ char *data;
+
+ flen = LengthResFile (f);
+
+ data = malloc (flen + 1);
+ if (!data) {
+ return;
+ }
+
+ // We may end up with less bytes than we asked for due to the
+ // DOS->Unix newline conversion
+ flen = ReadResFile (data, 1, flen, f);
+ data[flen] = '\0';
+
+ PropFile_from_string (data, handler, prefix);
+ free (data);
+}
+
+void
+PropFile_from_filename (uio_DirHandle *path, const char *fname, PROPERTY_HANDLER handler, const char *prefix)
+{
+ uio_Stream *f = res_OpenResFile (path, fname, "rt");
+ if (!f) {
+ return;
+ }
+ PropFile_from_file (f, handler, prefix);
+ res_CloseResFile(f);
+}
diff --git a/src/libs/resource/propfile.h b/src/libs/resource/propfile.h
new file mode 100644
index 0000000..edc8c36
--- /dev/null
+++ b/src/libs/resource/propfile.h
@@ -0,0 +1,30 @@
+/* propfile.h, Copyright (c) 2008 Michael C. Martin */
+
+/*
+ * 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 thta it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Se 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 PROPFILE_H_
+#define PROPFILE_H_
+
+#include "libs/uio.h"
+
+typedef void (*PROPERTY_HANDLER) (const char *, const char *);
+
+void PropFile_from_string (char *d, PROPERTY_HANDLER handler, const char *prefix);
+void PropFile_from_file (uio_Stream *f, PROPERTY_HANDLER handler, const char *prefix);
+void PropFile_from_filename (uio_DirHandle *path, const char *fname, PROPERTY_HANDLER handler, const char *prefix);
+
+#endif
diff --git a/src/libs/resource/resinit.c b/src/libs/resource/resinit.c
new file mode 100644
index 0000000..dacbee4
--- /dev/null
+++ b/src/libs/resource/resinit.c
@@ -0,0 +1,651 @@
+//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 "resintrn.h"
+#include "libs/memlib.h"
+#include "options.h"
+#include "types.h"
+#include "libs/log.h"
+#include "libs/gfxlib.h"
+#include "libs/reslib.h"
+#include "libs/sndlib.h"
+#include "libs/vidlib.h"
+#include "propfile.h"
+#include <ctype.h>
+#include <stdlib.h>
+// XXX: we should not include anything from uqm/ inside libs/
+#include "uqm/coderes.h"
+
+static RESOURCE_INDEX
+allocResourceIndex (void) {
+ RESOURCE_INDEX ndx = HMalloc (sizeof (RESOURCE_INDEX_DESC));
+ ndx->map = CharHashTable_newHashTable (NULL, NULL, NULL, NULL, NULL,
+ 0, 0.85, 0.9);
+ return ndx;
+}
+
+static void
+freeResourceIndex (RESOURCE_INDEX h) {
+ if (h != NULL)
+ {
+ /* TODO: This leaks the contents of h->map */
+ CharHashTable_deleteHashTable (h->map);
+ HFree (h);
+ }
+}
+
+#define TYPESIZ 32
+
+static ResourceDesc *
+newResourceDesc (const char *res_id, const char *resval)
+{
+ const char *path;
+ int pathlen;
+ ResourceHandlers *vtable;
+ ResourceDesc *result, *handlerdesc;
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ char typestr[TYPESIZ];
+
+ path = strchr (resval, ':');
+ if (path == NULL)
+ {
+ log_add (log_Warning, "Could not find type information for resource '%s'", res_id);
+ strncpy(typestr, "sys.UNKNOWNRES", TYPESIZ);
+ path = resval;
+ }
+ else
+ {
+ int n = path - resval;
+
+ if (n >= TYPESIZ - 4)
+ {
+ n = TYPESIZ - 5;
+ }
+ strncpy (typestr, "sys.", TYPESIZ);
+ strncat (typestr+1, resval, n);
+ typestr[n+4] = '\0';
+ path++;
+ }
+ pathlen = strlen (path);
+
+ handlerdesc = lookupResourceDesc(idx, typestr);
+ if (handlerdesc == NULL) {
+ path = resval;
+ log_add (log_Warning, "Illegal type '%s' for resource '%s'; treating as UNKNOWNRES", typestr, res_id);
+ handlerdesc = lookupResourceDesc(idx, "sys.UNKNOWNRES");
+ }
+
+ vtable = (ResourceHandlers *)handlerdesc->resdata.ptr;
+
+ if (vtable->loadFun == NULL)
+ {
+ log_add (log_Warning, "Warning: Unable to load '%s'; no handler "
+ "for type %s defined.", res_id, typestr);
+ return NULL;
+ }
+
+ result = HMalloc (sizeof (ResourceDesc));
+ if (result == NULL)
+ return NULL;
+
+ result->fname = HMalloc (pathlen + 1);
+ strncpy (result->fname, path, pathlen);
+ result->fname[pathlen] = '\0';
+ result->vtable = vtable;
+ result->refcount = 0;
+
+ if (vtable->freeFun == NULL)
+ {
+ /* Non-heap resources are raw values. Work those out at load time. */
+ vtable->loadFun (result->fname, &result->resdata);
+ }
+ else
+ {
+ result->resdata.ptr = NULL;
+ }
+ return result;
+}
+
+static void
+process_resource_desc (const char *key, const char *value)
+{
+ CharHashTable_HashTable *map = _get_current_index_header ()->map;
+ ResourceDesc *newDesc = newResourceDesc (key, value);
+ if (newDesc != NULL)
+ {
+ if (!CharHashTable_add (map, key, newDesc))
+ {
+ res_Remove (key);
+ CharHashTable_add (map, key, newDesc);
+ }
+ }
+}
+
+static void
+UseDescriptorAsRes (const char *descriptor, RESOURCE_DATA *resdata)
+{
+ resdata->str = descriptor;
+}
+
+static void
+DescriptorToInt (const char *descriptor, RESOURCE_DATA *resdata)
+{
+ resdata->num = atoi (descriptor);
+}
+
+static void
+DescriptorToBoolean (const char *descriptor, RESOURCE_DATA *resdata)
+{
+ if (!strcasecmp (descriptor, "true"))
+ {
+ resdata->num = TRUE;
+ }
+ else
+ {
+ resdata->num = FALSE;
+ }
+}
+
+static inline size_t
+skipWhiteSpace (const char *start)
+{
+ const char *ptr = start;
+ while (isspace (*ptr))
+ ptr++;
+ return (ptr - start);
+}
+
+// On success, resdata->num will be filled with a 32-bits RGBA value.
+static void
+DescriptorToColor (const char *descriptor, RESOURCE_DATA *resdata)
+{
+ int bytesParsed;
+ int componentBits;
+ int maxComponentValue;
+ size_t componentCount;
+ size_t compI;
+ int comps[4];
+ // One element for each of r, g, b, a.
+
+ descriptor += skipWhiteSpace (descriptor);
+
+#if 0
+ // Can't use this; '#' starts a comment.
+ if (*descriptor == '#')
+ {
+ // "#rrggbb"
+ int i;
+ DWORD value = 0;
+
+ descriptor++;
+ for (i = 0; i < 6; i++)
+ {
+ BYTE nibbleValue;
+ if (*descriptor >= '0' && *descriptor <= '9')
+ nibbleValue = *descriptor - '0';
+ else if (*descriptor >= 'a' && *descriptor <= 'f')
+ nibbleValue = 0xa + *descriptor - 'a';
+ else if (*descriptor >= 'A' && *descriptor <= 'F')
+ nibbleValue = 0xa + *descriptor - 'A';
+ else
+ goto fail;
+
+ value = (value * 16) + nibbleValue;
+ descriptor++;
+ }
+
+ descriptor += skipWhiteSpace (descriptor);
+
+ if (*descriptor != '\0')
+ log_add (log_Warning, "Junk after color resource string.");
+
+ resdata->num = (value << 8) | 0xff;
+ return;
+ }
+#endif
+
+ // Color is of the form "rgb(r, g, b)", "rgba(r, g, b, a)",
+ // or "rgb15(r, g, b)".
+
+ if (sscanf (descriptor, "rgb ( %i , %i , %i ) %n",
+ &comps[0], &comps[1], &comps[2], &bytesParsed) >= 3)
+ {
+ componentBits = 8;
+ componentCount = 3;
+ comps[3] = 0xff;
+ }
+ else if (sscanf (descriptor, "rgba ( %i , %i , %i , %i ) %n",
+ &comps[0], &comps[1], &comps[2], &comps[3], &bytesParsed) >= 4)
+ {
+ componentBits = 8;
+ componentCount = 4;
+ }
+ else if (sscanf (descriptor, "rgb15 ( %i , %i , %i ) %n",
+ &comps[0], &comps[1], &comps[2], &bytesParsed) >= 3)
+ {
+ componentBits = 5;
+ componentCount = 3;
+ comps[3] = 0xff;
+ }
+ else
+ goto fail;
+
+ if (descriptor[bytesParsed] != '\0')
+ log_add (log_Warning, "Junk after color resource string.");
+
+ maxComponentValue = (1 << componentBits) - 1;
+
+ // Check the range of the components.
+ for (compI = 0; compI < componentCount; compI++)
+ {
+ if (comps[compI] < 0)
+ {
+ comps[compI] = 0;
+ log_add (log_Warning, "Color component value too small; "
+ "value clipped.");
+ }
+
+ if (comps[compI] > (long) maxComponentValue)
+ {
+ comps[compI] = maxComponentValue;
+ log_add (log_Warning, "Color component value too large; "
+ "value clipped.");
+ }
+ }
+
+ if (componentBits == 5)
+ resdata->num = ((CC5TO8 (comps[0]) << 24) |
+ (CC5TO8 (comps[1]) << 16) | (CC5TO8 (comps[2]) << 8) |
+ comps[3]);
+ else
+ resdata->num = ((comps[0] << 24) | (comps[1] << 16) |
+ (comps[2] << 8) | comps[3]);
+
+ return;
+
+fail:
+ log_add (log_Error, "Invalid color description string for resource.\n");
+ resdata->num = 0x00000000;
+}
+
+static void
+RawDescriptor (RESOURCE_DATA *resdata, char *buf, unsigned int size)
+{
+ snprintf (buf, size, "%s", resdata->str);
+}
+
+static void
+IntToString (RESOURCE_DATA *resdata, char *buf, unsigned int size)
+{
+ snprintf (buf, size, "%d", resdata->num);
+}
+
+
+static void
+BooleanToString (RESOURCE_DATA *resdata, char *buf, unsigned int size)
+{
+ snprintf (buf, size, "%s", resdata->num ? "true" : "false");
+}
+
+static void
+ColorToString (RESOURCE_DATA *resdata, char *buf, unsigned int size)
+{
+ if ((resdata->num & 0xff) == 0xff)
+ {
+ // Opaque color, save as "rgb".
+ snprintf (buf, size, "rgb(0x%02x, 0x%02x, 0x%02x)",
+ (resdata->num >> 24), (resdata->num >> 16) & 0xff,
+ (resdata->num >> 8) & 0xff);
+ }
+ else
+ {
+ // (Partially) transparent color, save as "rgba".
+ snprintf (buf, size, "rgba(0x%02x, 0x%02x, 0x%02x, 0x%02x)",
+ (resdata->num >> 24), (resdata->num >> 16) & 0xff,
+ (resdata->num >> 8) & 0xff, resdata->num & 0xff);
+ }
+}
+
+static RESOURCE_INDEX curResourceIndex;
+
+void
+_set_current_index_header (RESOURCE_INDEX newResourceIndex)
+{
+ curResourceIndex = newResourceIndex;
+}
+
+RESOURCE_INDEX
+InitResourceSystem (void)
+{
+ RESOURCE_INDEX ndx;
+ if (curResourceIndex) {
+ return curResourceIndex;
+ }
+ ndx = allocResourceIndex ();
+
+ _set_current_index_header (ndx);
+
+ InstallResTypeVectors ("UNKNOWNRES", UseDescriptorAsRes, NULL, NULL);
+ InstallResTypeVectors ("STRING", UseDescriptorAsRes, NULL, RawDescriptor);
+ InstallResTypeVectors ("INT32", DescriptorToInt, NULL, IntToString);
+ InstallResTypeVectors ("BOOLEAN", DescriptorToBoolean, NULL,
+ BooleanToString);
+ InstallResTypeVectors ("COLOR", DescriptorToColor, NULL, ColorToString);
+ InstallGraphicResTypes ();
+ InstallStringTableResType ();
+ InstallAudioResTypes ();
+ InstallVideoResType ();
+ InstallCodeResType ();
+
+ return ndx;
+}
+
+RESOURCE_INDEX
+_get_current_index_header (void)
+{
+ if (!curResourceIndex) {
+ InitResourceSystem ();
+ }
+ return curResourceIndex;
+}
+
+void
+LoadResourceIndex (uio_DirHandle *dir, const char *rmpfile, const char *prefix)
+{
+ PropFile_from_filename (dir, rmpfile, process_resource_desc, prefix);
+}
+
+void
+SaveResourceIndex (uio_DirHandle *dir, const char *rmpfile, const char *root, BOOLEAN strip_root)
+{
+ uio_Stream *f;
+ CharHashTable_Iterator *it;
+ unsigned int prefix_len;
+
+ f = res_OpenResFile (dir, rmpfile, "wb");
+ if (!f) {
+ /* TODO: Warning message */
+ return;
+ }
+ prefix_len = root ? strlen (root) : 0;
+ for (it = CharHashTable_getIterator (_get_current_index_header ()->map);
+ !CharHashTable_iteratorDone (it);
+ it = CharHashTable_iteratorNext (it)) {
+ char *key = CharHashTable_iteratorKey (it);
+ if (!root || !strncmp (root, key, prefix_len)) {
+ ResourceDesc *value = CharHashTable_iteratorValue (it);
+ if (!value) {
+ log_add(log_Warning, "Resource %s had no value", key);
+ } else if (!value->vtable) {
+ log_add(log_Warning, "Resource %s had no type", key);
+ } else if (value->vtable->toString) {
+ char buf[256];
+ value->vtable->toString (&value->resdata, buf, 256);
+ buf[255]=0;
+ if (root && strip_root) {
+ WriteResFile (key+prefix_len, 1, strlen (key) - prefix_len, f);
+ } else {
+ WriteResFile (key, 1, strlen (key), f);
+ }
+ PutResFileChar(' ', f);
+ PutResFileChar('=', f);
+ PutResFileChar(' ', f);
+ WriteResFile (value->vtable->resType, 1, strlen (value->vtable->resType), f);
+ PutResFileChar(':', f);
+ WriteResFile (buf, 1, strlen (buf), f);
+ PutResFileNewline(f);
+ }
+ }
+ }
+ res_CloseResFile (f);
+ CharHashTable_freeIterator (it);
+}
+
+void
+UninitResourceSystem (void)
+{
+ freeResourceIndex (_get_current_index_header ());
+ _set_current_index_header (NULL);
+}
+
+BOOLEAN
+InstallResTypeVectors (const char *resType, ResourceLoadFun *loadFun,
+ ResourceFreeFun *freeFun, ResourceStringFun *stringFun)
+{
+ ResourceHandlers *handlers;
+ ResourceDesc *result;
+ char key[TYPESIZ];
+ int typelen;
+ CharHashTable_HashTable *map;
+
+ snprintf(key, TYPESIZ, "sys.%s", resType);
+ key[TYPESIZ-1] = '\0';
+ typelen = strlen(resType);
+
+ handlers = HMalloc (sizeof (ResourceHandlers));
+ if (handlers == NULL)
+ {
+ return FALSE;
+ }
+ handlers->loadFun = loadFun;
+ handlers->freeFun = freeFun;
+ handlers->toString = stringFun;
+ handlers->resType = resType;
+
+ result = HMalloc (sizeof (ResourceDesc));
+ if (result == NULL)
+ return FALSE;
+
+ result->fname = HMalloc (strlen(resType) + 1);
+ strncpy (result->fname, resType, typelen);
+ result->fname[typelen] = '\0';
+ result->vtable = NULL;
+ result->resdata.ptr = handlers;
+
+ map = _get_current_index_header ()->map;
+ return CharHashTable_add (map, key, result) != 0;
+}
+
+/* These replace the mapres.c calls and probably should be split out at some point. */
+BOOLEAN
+res_IsString (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ return desc && !strcmp(desc->vtable->resType, "STRING");
+}
+
+const char *
+res_GetString (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || !desc->resdata.str || strcmp(desc->vtable->resType, "STRING"))
+ return "";
+ /* TODO: Work out exact STRING semantics, specifically, the lifetime of
+ * the returned value. If caller is allowed to reference the returned
+ * value forever, STRING has to be ref-counted. */
+ return desc->resdata.str;
+}
+
+void
+res_PutString (const char *key, const char *value)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ int srclen, dstlen;
+ if (!desc || !desc->resdata.str || strcmp(desc->vtable->resType, "STRING"))
+ {
+ /* TODO: This is kind of roundabout. We can do better by refactoring newResourceDesc */
+ process_resource_desc(key, "STRING:undefined");
+ desc = lookupResourceDesc (idx, key);
+ }
+ srclen = strlen (value);
+ dstlen = strlen (desc->fname);
+ if (srclen > dstlen) {
+ char *newValue = HMalloc(srclen + 1);
+ char *oldValue = desc->fname;
+ log_add(log_Warning, "Reallocating string space for '%s'", key);
+ strncpy (newValue, value, srclen + 1);
+ desc->resdata.str = newValue;
+ desc->fname = newValue;
+ HFree (oldValue);
+ } else {
+ strncpy (desc->fname, value, dstlen + 1);
+ }
+}
+
+BOOLEAN
+res_IsInteger (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ return desc && !strcmp(desc->vtable->resType, "INT32");
+}
+
+int
+res_GetInteger (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "INT32"))
+ {
+ // TODO: Better error handling
+ return 0;
+ }
+ return desc->resdata.num;
+}
+
+void
+res_PutInteger (const char *key, int value)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "INT32"))
+ {
+ /* TODO: This is kind of roundabout. We can do better by refactoring newResourceDesc */
+ process_resource_desc(key, "INT32:0");
+ desc = lookupResourceDesc (idx, key);
+ }
+ desc->resdata.num = value;
+}
+
+BOOLEAN
+res_IsBoolean (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ return desc && !strcmp(desc->vtable->resType, "BOOLEAN");
+}
+
+BOOLEAN
+res_GetBoolean (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "BOOLEAN"))
+ {
+ // TODO: Better error handling
+ return FALSE;
+ }
+ return desc->resdata.num ? TRUE : FALSE;
+}
+
+void
+res_PutBoolean (const char *key, BOOLEAN value)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "BOOLEAN"))
+ {
+ /* TODO: This is kind of roundabout. We can do better by refactoring newResourceDesc */
+ process_resource_desc(key, "BOOLEAN:false");
+ desc = lookupResourceDesc (idx, key);
+ }
+ desc->resdata.num = value;
+}
+
+BOOLEAN
+res_IsColor (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ return desc && !strcmp(desc->vtable->resType, "COLOR");
+}
+
+Color
+res_GetColor (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ DWORD num;
+ if (!desc || strcmp(desc->vtable->resType, "COLOR"))
+ {
+ // TODO: Better error handling
+ return buildColorRgba (0, 0, 0, 0);
+ }
+
+ num = desc->resdata.num;
+ return buildColorRgba (num >> 24, (num >> 16) & 0xff,
+ (desc->resdata.num >> 8) & 0xff, num & 0xff);
+}
+
+void
+res_PutColor (const char *key, Color value)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "COLOR"))
+ {
+ /* TODO: This is kind of roundabout. We can do better by refactoring
+ * newResourceDesc */
+ process_resource_desc(key, "COLOR:rgb(0, 0, 0)");
+ desc = lookupResourceDesc (idx, key);
+ }
+ desc->resdata.num =
+ (value.r << 24) | (value.g << 16) | (value.b << 8) | value.a;
+}
+
+BOOLEAN
+res_HasKey (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ return (lookupResourceDesc(idx, key) != NULL);
+}
+
+BOOLEAN
+res_Remove (const char *key)
+{
+ CharHashTable_HashTable *map = _get_current_index_header ()->map;
+ ResourceDesc *oldDesc = (ResourceDesc *)CharHashTable_find (map, key);
+ if (oldDesc != NULL)
+ {
+ if (oldDesc->resdata.ptr != NULL)
+ {
+ if (oldDesc->refcount > 0)
+ log_add (log_Warning, "WARNING: Replacing '%s' while it is live", key);
+ if (oldDesc->vtable && oldDesc->vtable->freeFun)
+ {
+ oldDesc->vtable->freeFun(oldDesc->resdata.ptr);
+ }
+ }
+ HFree (oldDesc->fname);
+ HFree (oldDesc);
+ }
+ return CharHashTable_remove (map, key);
+}
diff --git a/src/libs/resource/resintrn.h b/src/libs/resource/resintrn.h
new file mode 100644
index 0000000..e2255ea
--- /dev/null
+++ b/src/libs/resource/resintrn.h
@@ -0,0 +1,34 @@
+//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_RESOURCE_RESINTRN_H_
+#define LIBS_RESOURCE_RESINTRN_H_
+
+#include <string.h>
+#include "libs/reslib.h"
+#include "index.h"
+
+ResourceDesc *lookupResourceDesc (RESOURCE_INDEX idx, RESOURCE res);
+void loadResourceDesc (ResourceDesc *desc);
+
+void _set_current_index_header (RESOURCE_INDEX newResourceIndex);
+RESOURCE_INDEX _get_current_index_header (void);
+
+
+#endif /* LIBS_RESOURCE_RESINTRN_H_ */
+
diff --git a/src/libs/resource/stringbank.c b/src/libs/resource/stringbank.c
new file mode 100644
index 0000000..a1b9576
--- /dev/null
+++ b/src/libs/resource/stringbank.c
@@ -0,0 +1,181 @@
+/* stringbank.c, Copyright (c) 2005 Michael C. Martin */
+
+/*
+ * 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 thta it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Se 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "stringbank.h"
+
+typedef stringbank chunk;
+
+static stringbank *
+add_chunk (stringbank *bank)
+{
+ stringbank *n = malloc (sizeof (stringbank));
+ n->len = 0;
+ n->next = NULL;
+ if (bank)
+ {
+ while (bank->next)
+ bank = bank->next;
+ bank->next = n;
+ }
+ return n;
+}
+
+stringbank *
+StringBank_Create (void)
+{
+ return add_chunk (NULL);
+}
+
+void
+StringBank_Free (stringbank *bank)
+{
+ if (bank)
+ {
+ StringBank_Free (bank->next);
+ free (bank);
+ }
+}
+
+const char *
+StringBank_AddString (stringbank *bank, const char *str)
+{
+ unsigned int len = strlen (str) + 1;
+ stringbank *x = bank;
+ if (len > STRBANK_CHUNK_SIZE)
+ return NULL;
+ while (x) {
+ unsigned int remaining = STRBANK_CHUNK_SIZE - x->len;
+ if (len < remaining) {
+ char *result = x->data + x->len;
+ strcpy (result, str);
+ x->len += len;
+ return result;
+ }
+ x = x->next;
+ }
+ /* No room in any currently existing chunk */
+ x = add_chunk (bank);
+ strcpy (x->data, str);
+ x->len += len;
+ return x->data;
+}
+
+const char *
+StringBank_AddOrFindString (stringbank *bank, const char *str)
+{
+ unsigned int len = strlen (str) + 1;
+ stringbank *x = bank;
+ if (len > STRBANK_CHUNK_SIZE)
+ return NULL;
+ while (x) {
+ int i = 0;
+ while (i < x->len) {
+ if (!strcmp (x->data + i, str))
+ return x->data + i;
+ while (x->data[i]) i++;
+ i++;
+ }
+ x = x->next;
+ }
+ /* We didn't find it, so add it */
+ return StringBank_AddString (bank, str);
+}
+
+static char buffer[STRBANK_CHUNK_SIZE];
+
+const char *
+StringBank_AddSubstring (stringbank *bank, const char *str, unsigned int n)
+{
+ unsigned int len = strlen (str);
+ if (n > len)
+ {
+ return StringBank_AddString (bank, str);
+ }
+ if (n >= STRBANK_CHUNK_SIZE)
+ {
+ return NULL;
+ }
+ strncpy (buffer, str, n);
+ buffer[n] = '\0';
+ return StringBank_AddString(bank, buffer);
+}
+
+const char *
+StringBank_AddOrFindSubstring (stringbank *bank, const char *str, unsigned int n)
+{
+ unsigned int len = strlen (str);
+ if (n > len)
+ {
+ return StringBank_AddOrFindString (bank, str);
+ }
+ if (n >= STRBANK_CHUNK_SIZE)
+ {
+ return NULL;
+ }
+ strncpy (buffer, str, n);
+ buffer[n] = '\0';
+ return StringBank_AddOrFindString(bank, buffer);
+}
+
+int
+SplitString (const char *s, char splitchar, int n, const char **result, stringbank *bank)
+{
+ int i;
+ const char *index = s;
+
+ for (i = 0; i < n-1; i++)
+ {
+ const char *next;
+ int len;
+
+ next = strchr (index, splitchar);
+ if (!next)
+ {
+ break;
+ }
+
+ len = next - index;
+ result[i] = StringBank_AddOrFindSubstring (bank, index, len);
+ index = next+1;
+ }
+ result[i] = StringBank_AddOrFindString (bank, index);
+ return i+1;
+}
+
+#ifdef SB_DEBUG
+
+void
+StringBank_Dump (stringbank *bank, FILE *s)
+{
+ stringbank *x = bank;
+ while (x) {
+ int i = 0;
+ while (i < x->len) {
+ fprintf (s, "\"%s\"\n", x->data + i);
+ while (x->data[i]) i++;
+ i++;
+ }
+ x = x->next;
+ }
+}
+
+#endif
diff --git a/src/libs/resource/stringbank.h b/src/libs/resource/stringbank.h
new file mode 100644
index 0000000..e77105b
--- /dev/null
+++ b/src/libs/resource/stringbank.h
@@ -0,0 +1,57 @@
+/* stringbank.h, Copyright (c) 2005 Michael C. Martin */
+
+/*
+ * 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 thta it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Se 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_RESOURCE_STRINGBANK_H_
+#define LIBS_RESOURCE_STRINGBANK_H_
+
+#ifdef SB_DEBUG
+#include <stdio.h>
+#endif
+
+#define STRBANK_CHUNK_SIZE (1024 - sizeof (void *) - sizeof (int))
+
+typedef struct _stringbank_chunk {
+ char data[STRBANK_CHUNK_SIZE];
+ int len;
+ struct _stringbank_chunk *next;
+} stringbank;
+
+/* Constructors and destructors */
+stringbank *StringBank_Create (void);
+void StringBank_Free (stringbank *bank);
+
+/* Put str or n chars after str into the string bank. */
+const char *StringBank_AddString (stringbank *bank, const char *str);
+const char *StringBank_AddSubstring (stringbank *bank, const char *str, unsigned int n);
+
+/* Put str or n chars after str into the string bank if it's not already
+ there. Much slower. */
+const char *StringBank_AddOrFindString (stringbank *bank, const char *str);
+const char *StringBank_AddOrFindSubstring (stringbank *bank, const char *str, unsigned int n);
+
+/* Split a string s into at most n substrings, separated by splitchar.
+ Pointers to these substrings will be stored in result; the
+ substrings themselves will be filed in the specified stringbank. */
+int SplitString (const char *s, char splitchar, int n, const char **result, stringbank *bank);
+
+#ifdef SB_DEBUG
+/* Print out a list of the contents of the string bank to the named stream. */
+void StringBank_Dump (stringbank *bank, FILE *s);
+#endif /* SB_DEBUG */
+
+#endif /* LIBS_RESOURCE_STRINGBANK_H_ */