summaryrefslogtreecommitdiff
path: root/src/libs/resource/resinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/resource/resinit.c')
-rw-r--r--src/libs/resource/resinit.c651
1 files changed, 651 insertions, 0 deletions
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);
+}