aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/gfx/gfx_res_options.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/gfx/gfx_res_options.cpp')
-rw-r--r--engines/sci/gfx/gfx_res_options.cpp652
1 files changed, 652 insertions, 0 deletions
diff --git a/engines/sci/gfx/gfx_res_options.cpp b/engines/sci/gfx/gfx_res_options.cpp
new file mode 100644
index 0000000000..0692fece0f
--- /dev/null
+++ b/engines/sci/gfx/gfx_res_options.cpp
@@ -0,0 +1,652 @@
+/***************************************************************************
+ gfx_res_options.c Copyright (C) 2002 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include "sci/include/gfx_system.h"
+#include "sci/include/gfx_options.h"
+#include "sci/include/gfx_resmgr.h"
+
+#include <ctype.h>
+
+/*#define DEBUG*/
+
+static gfx_res_pattern_list_t*
+pattern_list_insert(gfx_res_pattern_list_t *list, int min, int max)
+{
+ gfx_res_pattern_list_t *retval = (gfx_res_pattern_list_t*)sci_malloc(sizeof(gfx_res_pattern_list_t));
+ retval->pattern.min = min;
+ retval->pattern.max = max;
+ retval->next = list;
+
+ return retval;
+}
+
+static int
+pattern_list_len(gfx_res_pattern_list_t *list)
+{
+ int v = 0;
+ while (list) {
+ ++v;
+ list = list->next;
+ }
+
+ return v;
+}
+
+static void
+pattern_list_flatten(gfx_res_pattern_t *dest, gfx_res_pattern_list_t *list)
+{
+ while (list) {
+ *dest++ = list->pattern;
+ list = list->next;
+ }
+}
+
+static inline void
+pattern_list_free(gfx_res_pattern_list_t *list)
+{
+ if (list)
+ pattern_list_free(list->next);
+
+ free(list);
+}
+
+
+static inline int
+extract_pattern(gfx_res_pattern_list_t **destp,
+ char *src, int offset)
+{
+ char *src_orig = src - offset;
+ int final = 0;
+ int wildcard = 0;
+
+ while (!final) {
+ char *end = strchr(src, ',');
+ if (end)
+ *end = 0;
+ else
+ final = 1;
+
+ while (*src && isblank(*src))
+ ++src;
+
+ if (*src == '*'
+ || *src == '_') {
+ wildcard = 1;
+ ++src;
+ } else if (*src == '.' || isdigit(*src)) {
+ char *endp;
+ int start = strtol(src, &endp, 0);
+
+ if (*src == '.'
+ && src[1] == '.') {
+ start = GFX_RES_PATTERN_MIN;
+ endp = src;
+ }
+
+ src = endp;
+
+ while (*src && isblank(*src))
+ ++src;
+
+ if (*src) {
+ int stop;
+ if (*src == '.'
+ && src[1] == '.') {
+
+ src += 2;
+ while (*src && isblank(*src))
+ ++src;
+
+ if (!*src)
+ stop = GFX_RES_PATTERN_MAX;
+ else if (!isdigit(*src)) {
+ if (end)
+ *end = ',';
+ goto lexical_error_label;
+ }
+
+ stop = strtol(src, &endp, 0);
+ src = endp;
+
+ *destp = pattern_list_insert(*destp,
+ start, stop);
+
+ } else { /* No ellipsis */
+ if (end)
+ *end = ',';
+ goto lexical_error_label;
+ }
+ } else /* End of sub-pattern */
+ *destp = pattern_list_insert(*destp,
+ start, start);
+
+ while (*src && isblank(*src))
+ ++src;
+
+ if (*src) {
+ if (end)
+ *end = ',';
+ goto lexical_error_label;
+ }
+
+ } else {
+ if (end)
+ *end = ',';
+ sciprintf("[gfx-conf] Unexpected character '%c'\n",
+ *src);
+ goto lexical_error_label;
+ }
+
+ if (!final) {
+ *end = ',';
+ src = end + 1;
+ }
+ }
+
+ if (wildcard) {
+ pattern_list_free(*destp);
+ *destp = NULL;
+ }
+
+ return 0;
+
+ lexical_error_label:
+ sciprintf("[gfx-conf] Lexical error in pattern at offset %d\n",
+ src - src_orig);
+ return 1;
+}
+
+static int
+extract_mod_rule(char *src, gfx_res_mod_t *rule)
+{
+ char *orig_src = src;
+ char *endp;
+ float f[3];
+ int i;
+
+ rule->type = GFX_RES_MULTIPLY_FIXED;
+
+ if (isdigit(*src) || *src == '.') {
+ f[0] = f[1] = f[2] = strtod(src, &endp);
+
+ if (*endp)
+ goto mod_error_label;
+ } else if (*src == '(') {
+ i = 0;
+ ++src;
+
+ do {
+ while (*src && isblank(*src))
+ ++src;
+ if (!*src || !(isdigit(*src) || *src == '.')) {
+ sciprintf("[gfx-conf] Unexpected character '%c'\n",
+ *src);
+ goto mod_error_label;
+ }
+ f[i++] = strtod(src, &endp);
+
+ src = endp;
+
+ while (*src && isblank(*src))
+ ++src;
+
+ if ((i == 3) && *src != ')') {
+ sciprintf("[gfx-conf] Error: Expected ')' at end of modification rule\n");
+ goto mod_error_label;
+ } else if (i<3 && !isdigit(*src) && *src != '.' && *src != ',') {
+ sciprintf("[gfx-conf] Error: Expected ',' as separator in modification rule, not '%c'\n",
+ *src);
+ goto mod_error_label;
+ }
+ ++src;
+ } while (i < 3);
+
+ if (*src) {
+ sciprintf("[gfx-conf] Error: Trailing garbage after modification rule\n");
+ goto mod_error_label;
+ }
+
+ } else
+ goto mod_error_label;
+
+ for (i = 0; i < 3; i++) {
+ int v = (int)(f[i] * 16.0);
+ rule->mod.factor[i] = (v > 255) ? 255 : v;
+ }
+
+ return 0;
+ mod_error_label:
+ sciprintf("[gfx-conf] Ill-formed modification rule '%s'\n",
+ orig_src);
+ return 1;
+}
+
+
+extern gfx_pixmap_color_t gfx_sci0_image_colors[][GFX_SCI0_IMAGE_COLORS_NR];
+
+#define PREDEFINED_PALETTES_NR 4
+static int
+extract_assign_rule(char *src, gfx_res_assign_t *rule)
+{
+ /*char *orig_src = src;*/
+ struct {
+ const char *name;
+ int colors_nr;
+ gfx_pixmap_color_t *colors;
+ } predefined_palettes[PREDEFINED_PALETTES_NR] = {
+ {"default", 16, (gfx_pixmap_color_t *) &(gfx_sci0_image_colors[0])},
+ {"amiga", 16, (gfx_pixmap_color_t *) &(gfx_sci0_image_colors[1])},
+ {"gray", 16, (gfx_pixmap_color_t *) &(gfx_sci0_image_colors[2])},
+ {"grey", 16, (gfx_pixmap_color_t *) &(gfx_sci0_image_colors[2])},
+ };
+ int i;
+
+ rule->type = GFX_RES_ASSIGN_TYPE_PALETTE;
+
+ for (i = 0; i < PREDEFINED_PALETTES_NR; i++)
+ if (!strcmp(src, predefined_palettes[i].name)) {
+ rule->assign.palette.colors_nr =
+ predefined_palettes[i].colors_nr;
+ rule->assign.palette.colors =
+ predefined_palettes[i].colors;
+ return 0;
+ }
+
+ sciprintf("[gfx-conf] Unknown palette '%s'\n", src);
+ return 1;
+ /*
+ assign_error_label:
+ sciprintf("[gfx-conf] Ill-formed assignment rule '%s'\n",
+ orig_src);
+ return 1;
+ */
+}
+
+#define CLASSES_NR 3
+int
+gfx_update_conf(gfx_options_t *options, char *str)
+{
+ int total_patterns;
+ int mod = 0; /* Modifier or assignment rule? */
+ char *orig_str = str;
+ char *sem_end;
+
+ int fields_nr; /* Number of fields a restriction is possible by.
+ ** cursors:1, pics:2, views:3. */
+ struct {
+ const char *class_name;
+ int class_id;
+ int fields_nr;
+ } classes[CLASSES_NR] = {
+ {"view", GFX_RESOURCE_TYPE_VIEW, 3},
+ {"pic", GFX_RESOURCE_TYPE_PIC, 2},
+ {"cursor", GFX_RESOURCE_TYPE_CURSOR, 1},
+ };
+ gfx_res_conf_t *conf = (gfx_res_conf_t*)sci_malloc(sizeof(gfx_res_conf_t));
+ gfx_res_pattern_list_t *patterns = NULL;
+ gfx_res_pattern_list_t *loops = NULL;
+ gfx_res_pattern_list_t *cels = NULL;
+ gfx_res_pattern_list_t **fields[3] = {
+ &patterns, &loops, &cels
+ };
+ int i;
+ int fieldcnt;
+ const char *pat_name_str;
+
+ /* Extract pattern(s) */
+ while (*str && isblank(*str))
+ ++str;
+
+ fields_nr = -1;
+ for (i = 0; i < CLASSES_NR; i++) {
+ int len = strlen(classes[i].class_name);
+
+ if (!strncmp(str, classes[i].class_name, len)) {
+ pat_name_str = classes[i].class_name;
+ conf->type = classes[i].class_id;
+ fields_nr = classes[i].fields_nr;
+ str += len;
+ break;
+ }
+ }
+
+ if (fields_nr == -1) {
+ sciprintf("[gfx-conf] Unexpected pattern class: Expected one of 'view', 'pic', 'cursor'\n");
+ goto failure_label;
+ }
+
+ fieldcnt = 0;
+ do {
+ while (*str && isblank(*str))
+ ++str;
+
+ if (!*str)
+ goto unexpected_end;
+
+ if (*str == '='
+ || *str == '*')
+ break;
+
+ if (*str == '(') {
+ char *end = strchr(str, ')');
+
+ if (fieldcnt >= fields_nr) {
+ sciprintf("[gfx-conf] Error: Patterns of class '%s' may only be"
+ " constrained by %d arguments\n",
+ pat_name_str, fields_nr);
+ goto failure_label;
+ }
+
+ if (!end) {
+ sciprintf("[gfx-conf] Unmatched parentheses at offset %d\n",
+ str - orig_str);
+ goto failure_label;
+ }
+ *end = 0;
+
+ if (extract_pattern(fields[fieldcnt++],
+ str + 1,
+ str + 1 - orig_str))
+ goto failure_label;
+
+ *end = ')';
+ str = end + 1;
+
+ continue;
+ }
+
+ sciprintf("[gfx-conf] Lexical error in pattern at offset %d: Unexpected '%c'\n",
+ str - orig_str, *str);
+ goto failure_label;
+ } while (1);
+
+
+ /* Flatten patterns */
+ conf->patterns = NULL;
+ total_patterns = conf->patterns_nr = pattern_list_len(patterns);
+ total_patterns += (conf->loops_nr = pattern_list_len(loops));
+ total_patterns += (conf->cels_nr = pattern_list_len(cels));
+
+ conf->patterns = (gfx_res_pattern_t*)sci_malloc(1 + (sizeof(gfx_res_pattern_t) * total_patterns));
+ pattern_list_flatten(conf->patterns, patterns);
+ pattern_list_flatten(conf->patterns + conf->patterns_nr, loops);
+ pattern_list_flatten(conf->patterns + conf->patterns_nr + conf->loops_nr, cels);
+
+ pattern_list_free(patterns);
+ patterns = NULL;
+ pattern_list_free(loops);
+ loops = NULL;
+ pattern_list_free(cels);
+ cels = NULL;
+
+ /* Parse remainder */
+ if (*str == '*') {
+ mod = 1;
+ ++str;
+ }
+
+ if (*str != '=') {
+ sciprintf("[gfx-conf] Expected '='\n");
+ goto failure_label;
+ }
+
+ do { ++str; }
+ while (*str && isblank(*str));
+
+ sem_end = strchr(str, ';');
+ if (!sem_end) {
+ sciprintf("[gfx-conf] Expected ';' at end of rule\n");
+ goto failure_label;
+ }
+ do { *sem_end-- = 0; }
+ while (sem_end >= str
+ && isblank(*sem_end));
+
+ if (mod) {
+ if (extract_mod_rule(str, &conf->conf.mod))
+ goto failure_label;
+ } else {
+ if (extract_assign_rule(str, &conf->conf.assign))
+ goto failure_label;
+ }
+
+ /* Write back into options */
+ if (mod) {
+ conf->next = options->res_conf.mod[conf->type];
+ options->res_conf.mod[conf->type] = conf;
+ } else {
+ conf->next = options->res_conf.assign[conf->type];
+ options->res_conf.assign[conf->type] = conf;
+ }
+
+ return 0;
+
+ /* Error handling */
+unexpected_end:
+ sciprintf("[gfx-conf] Unexpected end of pattern encountered\n");
+ failure_label:
+ sciprintf("[gfx-conf] Error occured in: '%s'\n", orig_str);
+ pattern_list_free(patterns);
+ pattern_list_free(loops);
+ pattern_list_free(cels);
+ if (conf->patterns)
+ free(conf->patterns);
+ free(conf);
+ return 1;
+}
+
+static inline int
+matches_patternlist(gfx_res_pattern_t *patterns, int nr, int val)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ if (patterns[i].min <= val
+ && patterns[i].max >= val)
+ return 1;
+
+ return 0;
+}
+
+#ifdef DEBUG
+static void
+print_pattern(gfx_res_pattern_t *pat)
+{
+ fprintf(stderr, "[%d..%d]",
+ pat->min, pat->max);
+}
+#endif
+
+static inline int
+resource_matches_patternlists(gfx_res_conf_t *conf,
+ int type, int nr, int loop, int cel)
+{
+ int loc;
+#ifdef DEBUG
+ int i;
+ fprintf(stderr, "[DEBUG:gfx-res] Trying to match against %d/%d/%d choices\n",
+ conf->patterns_nr, conf->loops_nr, conf->cels_nr);
+ for (i = 0; i < conf->patterns_nr; i++) {
+ fprintf(stderr, "[DEBUG:gfx-res] Pat #%d: ", i);
+ print_pattern(conf->patterns + i);
+ fprintf(stderr, "\n");
+ }
+ loc = conf->patterns_nr;
+ for (i = 0; i < conf->loops_nr; i++) {
+ fprintf(stderr, "[DEBUG:gfx-res] Loop #%d: ", i);
+ print_pattern(conf->patterns + i + loc);
+ fprintf(stderr, "\n");
+ }
+ loc += conf->loops_nr;
+ for (i = 0; i < conf->cels_nr; i++) {
+ fprintf(stderr, "[DEBUG:gfx-res] Cel #%d: ", i);
+ print_pattern(conf->patterns + i + loc);
+ fprintf(stderr, "\n");
+ }
+#endif
+ if (conf->patterns_nr &&
+ !matches_patternlist(conf->patterns,
+ conf->patterns_nr,
+ nr))
+ return 0;
+
+ if (type == GFX_RESOURCE_TYPE_CURSOR)
+ return 1;
+
+ /* Otherwise, we must match at least the loop (pic)
+ ** and, for views, the cel as well */
+ loc = conf->patterns_nr;
+ if (conf->loops_nr &&
+ !matches_patternlist(conf->patterns + loc,
+ conf->loops_nr,
+ loop))
+ return 0;
+
+ if (type != GFX_RESOURCE_TYPE_VIEW)
+ return 1;
+
+ loc += conf->loops_nr;
+
+ if (!conf->cels_nr)
+ return 1;
+
+ return matches_patternlist(conf->patterns + loc,
+ conf->cels_nr,
+ cel);
+}
+
+static inline gfx_res_conf_t *
+find_match(gfx_res_conf_t *conflist,
+ int type, int nr, int loop, int cel)
+{
+ while (conflist) {
+ if (resource_matches_patternlists(conflist,
+ type, nr, loop, cel)) {
+#ifdef DEBUG
+ fprintf(stderr, "[DEBUG:gfx-res] Found match!\n");
+#endif
+ return conflist;
+ }
+
+ conflist = conflist->next;
+ }
+ return NULL;
+}
+
+void
+apply_assign(gfx_res_assign_t *conf, gfx_pixmap_t *pxm)
+{
+ /* Has a dynamically allocated palette? Must clean up */
+ if (!(pxm->flags & GFX_PIXMAP_FLAG_EXTERNAL_PALETTE)) {
+ if (pxm->colors)
+ free(pxm->colors);
+ pxm->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE;
+ }
+
+ pxm->colors_nr = conf->assign.palette.colors_nr;
+ pxm->colors = conf->assign.palette.colors;
+}
+
+void
+apply_mod(gfx_res_mod_t *mod, gfx_pixmap_t *pxm)
+{
+ gfx_pixmap_color_t *pal = pxm->colors;
+ int i, pal_size = pxm->colors_nr;
+
+ /* Does not have a dynamically allocated palette? Must dup current one */
+ if (pxm->flags & GFX_PIXMAP_FLAG_EXTERNAL_PALETTE) {
+ int size = sizeof(gfx_pixmap_color_t) * pal_size;
+ pxm->colors = (gfx_pixmap_color_t*)sci_malloc(size);
+ memcpy(pxm->colors, pal, size);
+ pal = pxm->colors;
+ pxm->flags &= ~GFX_PIXMAP_FLAG_EXTERNAL_PALETTE;
+ /* Flag for later deallocation */
+ }
+
+ switch (mod->type) {
+
+ case GFX_RES_MULTIPLY_FIXED: {
+ for (i = 0; i < pal_size; i++) {
+ int v;
+
+#define UPDATE_COL(nm, idx) \
+ v = pal[i].nm; \
+ v *= mod->mod.factor[idx]; \
+ v >>= 4; \
+ pal[i].nm = (v > 255)? 255 : v;
+
+ UPDATE_COL(r, 0);
+ UPDATE_COL(g, 1);
+ UPDATE_COL(b, 2);
+#undef UPDATE_COL
+ }
+ break;
+ }
+
+ default:
+ GFXERROR("Using unexpected visual resource modifier %d\n", mod->type);
+ }
+}
+
+int
+gfx_get_res_config(gfx_options_t *options, gfx_pixmap_t *pxm)
+{
+ int restype = GFXR_RES_TYPE(pxm->ID);
+ int nr = GFXR_RES_NR(pxm->ID);
+ int loop = pxm->loop;
+ int cel = pxm->cel;
+
+ gfx_res_conf_t *conf;
+
+#ifdef DEBUG
+ fprintf(stderr, "[DEBUG:gfx-res] Trying to conf %d/%d/%d/%d (ID=%d)\n",
+ restype, nr, loop, cel, pxm->ID);
+#endif
+
+ if (pxm->ID < 0 || restype < 0 || restype >= GFX_RESOURCE_TYPES_NR)
+ return 1; /* Not appropriate */
+
+ conf = find_match(options->res_conf.assign[restype],
+ restype, nr, loop, cel);
+
+ if (conf)
+ apply_assign(&(conf->conf.assign), pxm);
+
+ conf = options->res_conf.mod[restype];
+ while (conf) {
+ conf = find_match(conf,
+ restype, nr, loop, cel);
+ if (conf) {
+ apply_mod(&(conf->conf.mod), pxm);
+ conf = conf->next;
+ }
+ }
+ fflush(NULL);
+
+ return 0;
+}