Index: /trunk/psModules/src/config/pmConfigMask.c
===================================================================
--- /trunk/psModules/src/config/pmConfigMask.c	(revision 19440)
+++ /trunk/psModules/src/config/pmConfigMask.c	(revision 19441)
@@ -9,16 +9,12 @@
 #include "pmConfigMask.h"
 
-
-psMaskType pmConfigMaskGet(const char *masks, const pmConfig *config)
-{
-    psAssert(config, "Require configuration");
-    PS_ASSERT_STRING_NON_EMPTY(masks, 0);
-
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
-    if (!recipe) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
-        return 0;
-    }
-
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static psMaskType maskGet(const psMetadata *source, // Source of masks
+                          const char *masks // Mask values to get
+                          )
+{
     psMaskType mask = 0;                // Mask value, to return
 
@@ -27,8 +23,7 @@
         const char *name = names->data[i]; // Symbolic name of interest
         bool mdok;                      // Status of MD lookup
-        psMaskType value = psMetadataLookupU8(&mdok, recipe, name);
+        psMaskType value = psMetadataLookupU8(&mdok, source, name);
         if (!mdok) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, false,
-                    "Unable to find mask value for %s in MASK recipe", name);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find mask value for %s", name);
             psFree(names);
             return 0;
@@ -41,102 +36,113 @@
 }
 
-bool pmConfigMaskSet(const pmConfig *config, const char *maskName, psMaskType maskValue)
-{
-    psAssert(config, "Require configuration");
-    PS_ASSERT_STRING_NON_EMPTY(maskName, false);
-
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
-    if (!recipe) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
-        return false;
-    }
-
-    psMetadataAddU8 (recipe, PS_LIST_TAIL, maskName, PS_META_REPLACE, "user-defined mask", maskValue);
-
-    return true;
-}
-
-// replace the named masks in the recipe with values in the header:
-// replace only the names in the header in the recipe
-bool pmConfigMaskReadHeader(pmConfig *config, const psMetadata *header)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-    PS_ASSERT_METADATA_NON_NULL(header, false);
-
-    bool status = false;
-
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
-    if (!recipe) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
-        return false;
-    }
-
-    // MASK.VALUE and MARK.VALUE aren't usually set in the recipe, but may be set in the header: create fake
-    // versions so that it won't complain later
-    if (!psMetadataLookup(recipe, "MASK.VALUE")) {
-        psMetadataAddU8(recipe, PS_LIST_TAIL, "MASK.VALUE", 0, "Bits to mask", 0);
-    }
-    if (!psMetadataLookup(recipe, "MARK.VALUE")) {
-        psMetadataAddU8(recipe, PS_LIST_TAIL, "MARK.VALUE", 0, "Bits for marking", 0);
-    }
-
-    int nMask = psMetadataLookupS32(&status, header, "MSKNUM");
-    if (!status) {
-        if (psMetadataLookupBool(&status, config->camera, "MASK.FORCE")) {
-            psWarning("No mask values in header.  Assuming MASKS recipe is accurate because of MASK.FORCE");
-            return true;
-        }
-        psError(PS_ERR_UNKNOWN, true, "Unable to find MSKNUM in header.");
-        return false;
-    }
-
-    char namekey[80];                   // Keyword name for symbolic name of mask entry
-    char valuekey[80];                  // Keyword name for value of mask entry
-    for (int i = 0; i < nMask; i++) {
-        snprintf(namekey,  64, "MSKNAM%02d", i);
-        snprintf(valuekey, 64, "MSKVAL%02d", i);
-
-        char *name = psMetadataLookupStr(&status, header, namekey);
-        if (!status || !name) {
-            psWarning("Unable to find header keyword %s when parsing mask", namekey);
-            continue;
-        }
-        psU8 bit = psMetadataLookupU8(&status, header, valuekey);
-        if (!status) {
-            psWarning("Unable to find header keyword %s when parsing mask", namekey);
-            continue;
-        }
-
-        // XXX validate that bit is a 2^n value?
-
-        psString nameAlready = NULL;    // Name of key with ".ALREADY" added
-        psStringAppend(&nameAlready, "%s.ALREADY", name);
-        bool already = psMetadataLookupBool(&status, recipe, nameAlready); // Already read this one?
-
-        psMetadataItem *item = psMetadataLookup(recipe, name); // Item in recipe with current value
-        if (item && item->type != PS_TYPE_MASK) {
-            psWarning("Mask recipe entry is not of a mask type (%x)", item->type);
-            item->type = PS_TYPE_MASK;
-        }
-
-        if (already) {
-            if (item && item->data.U8 != bit) {
-                psWarning("New mask recipe entry doesn't match previously loaded entry: %x vs %x",
-                          bit, item->data.U8);
+// maskSetValues examine named mask values and set the bits for maskValue and markValue.
+// Ensures that the below-named bad mask values are set, and calculates the mask value to catch them all
+// Ensure that the below-named other mask values are set (to 0x00 if necessary)
+
+// List of mask names for "bad" (i.e., mask me please) pixels
+static const char *badMaskNames[] = { "DETECTOR", // Something is wrong with the detector
+                                      "DARK", // Pixel doesn't dark-subtract properly
+                                      "FLAT", // Pixel doesn't flat-field properly
+                                      "BLANK", // Pixel doesn't contain valid data
+                                      "RANGE",// Pixel is out-of-range of linearity
+                                      "SAT",  // Pixel is saturated
+                                      "LOW",  // Pixel is low
+                                      "CONV", // Pixel is bad after convolution with a bad pixel
+                                      "CR",   // Pixel contains a cosmic ray
+                                      "GHOST",// Pixel contains an optical ghost
+                                      NULL // End marker
+};
+// Fallback names in case a bad mask name is not defined
+static const char *fallbackMaskNames[] = { NULL, // DETECTOR
+                                           "DETECTOR", // DARK
+                                           "DETECTOR", // FLAT
+                                           "DETECTOR", // BLANK
+                                           NULL, // RANGE
+                                           "RANGE", // SAT
+                                           "RANGE", // LOW
+                                           NULL, // CONV
+                                           NULL, // CR
+                                           NULL, // GHOST
+};
+// Default values in case a bad mask name and its fallback is not defined
+static const psMaskType defaultMask[] = { 0x00, // DETECTOR
+                                          0x00, // DARK
+                                          0x01, // FLAT
+                                          0x01, // BLANK
+                                          0x00, // RANGE
+                                          0x01, // SAT
+                                          0x01, // LOW
+                                          0x01, // CONV
+                                          0x00, // CR
+                                          0x00  // GHOST
+};
+// Other mask names to ensure exist; these shouldn't be combined in the MASK.VALUE
+static const char *otherMaskNames[] = { "POOR", // Pixel is poor after convolution with a bad pixel
+                                        NULL // End marker
+};
+
+static bool maskSetValues(psMaskType *outMaskValue, // Value of MASK.VALUE, returned
+                          psMaskType *outMarkValue, // Value of MARK.VALUE, returned
+                          psMetadata *source  // Source of mask bits
+                          )
+{
+    PS_ASSERT_METADATA_NON_NULL(source, false);
+
+    // Ensure all the bad mask names exist, and set the value to catch all bad pixels
+    psMaskType maskValue = 0;           // Value to mask to catch all the bad pixels
+    for (int i = 0; badMaskNames[i]; i++) {
+        const char *name = badMaskNames[i]; // Name for mask
+        const char *fallback = fallbackMaskNames[i]; // Fallback for mask
+
+        bool mdok;                      // Status of MD lookup
+        psMaskType value = psMetadataLookupU8(&mdok, source, name); // Value of mask
+        if (!value) {
+            if (fallback) {
+                value = psMetadataLookupU8(&mdok, source, fallback);
             }
+            if (!value) {
+                value = defaultMask[i];
+            }
+            psMetadataAddU8(source, PS_LIST_TAIL, name, PS_META_REPLACE, NULL, value);
+        }
+        maskValue |= value;
+    }
+
+    // Ensure all the other mask names exist
+    for (int i = 0; otherMaskNames[i]; i++) {
+        const char *name = otherMaskNames[i]; // Name for mask
+        bool mdok;                      // Status of MD lookup
+        psMaskType value = psMetadataLookupU8(&mdok, source, name); // Value of mask
+        if (!value) {
+            psMetadataAddU8(source, PS_LIST_TAIL, name, PS_META_REPLACE, NULL, 0x00);
+        }
+    }
+
+    // search for an unset bit to use for MARK:
+    psMaskType markValue = 0x80;
+
+    int nBits = sizeof(psMaskType) * 8;
+    for (int i = 0; !markValue && (i < nBits); i++) {
+        if (maskValue & markValue) {
+            markValue >>= 1;
         } else {
-            psMetadataAddBool(recipe, PS_LIST_TAIL, nameAlready, 0, "Already read this mask value", true);
-        }
-
-        if (!item) {
-            psWarning("Mask recipe entry %s not in recipe\n", name);
-            psMetadataAddU8(recipe, PS_LIST_TAIL, name, 0, "Bitmask bit value", bit);
-        } else {
-            item->data.U8 = bit;
-        }
-
-        psFree(nameAlready);
-    }
-
+            markValue = markValue;
+        }
+    }
+    if (!markValue) {
+        psError (PS_ERR_UNKNOWN, true, "Unable to define the MARK bit mask: all bits taken!");
+        return false;
+    }
+
+    // update the list with the results
+    psMetadataAddU8(source, PS_LIST_TAIL, "MASK.VALUE", PS_META_REPLACE, NULL, maskValue);
+    psMetadataAddU8(source, PS_LIST_TAIL, "MARK.VALUE", PS_META_REPLACE, NULL, markValue);
+
+    if (outMaskValue) {
+        *outMaskValue = maskValue;
+    }
+    if (outMarkValue) {
+        *outMarkValue = markValue;
+    }
 
     return true;
@@ -162,10 +168,122 @@
 }
 
-
-// write the named mask bits to the header
-bool pmConfigMaskWriteHeader(const pmConfig *config, psMetadata *header)
-{
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FPA version of mask functions.  These are not ready to go yet.
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if 0
+bool pmFPAMaskSetValues(psMaskType *outMaskValue, psMaskType *outMarkValue, pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+
+    return maskSetValues(outMaskValue, outMarkValue, fpa->masks);
+}
+
+psMaskType pmFPAMaskGet(const pmFPA *fpa, const char *masks, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, 0);
+    PS_ASSERT_STRING_NON_EMPTY(masks, 0);
+    PS_ASSERT_PTR_NON_NULL(config, 0);
+
+    if (fpa->masks) {
+        return maskGet(fpa->masks, masks);
+    }
+    return pmConfigMaskGet(masks, config);
+}
+
+bool pmFPAMaskSet(pmFPA *fpa, const char *maskName, psMaskType maskValue)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, 0);
+    PS_ASSERT_STRING_NON_EMPTY(maskName, false);
+
+    if (!fpa->masks) {
+        fpa->masks = psMetadataAlloc();
+    }
+    return psMetadataAddU8(fpa->masks, PS_LIST_TAIL, maskName, PS_META_REPLACE, NULL, maskValue);
+}
+
+bool pmFPAMaskReadHeader(pmFPA *fpa, const psMetadata *header, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_METADATA_NON_NULL(header, false);
     PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if (!fpa->masks) {
+        fpa->masks = psMetadataAlloc();
+    }
+
+    bool mdok;                          // Status of MD lookup
+    int numMask = psMetadataLookupS32(&mdok, header, "MSKNUM"); // Number of mask values in header
+    if (!mdok) {
+        if (psMetadataLookupBool(&mdok, config->camera, "MASK.FORCE")) {
+            psWarning("No mask values in header.  Assuming MASKS recipe is accurate because of MASK.FORCE");
+            numMask = 0;
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "Unable to find MSKNUM in header.");
+            return false;
+        }
+    }
+
+    char namekey[80];                   // Keyword name for symbolic name of mask entry
+    char valuekey[80];                  // Keyword name for value of mask entry
+    for (int i = 0; i < numMask; i++) {
+        snprintf(namekey,  64, "MSKNAM%02d", i);
+        snprintf(valuekey, 64, "MSKVAL%02d", i);
+
+        char *name = psMetadataLookupStr(&mdok, header, namekey);
+        if (!mdok || !name) {
+            psWarning("Unable to find header keyword %s when parsing mask", namekey);
+            continue;
+        }
+        psU8 bit = psMetadataLookupU8(&mdok, header, valuekey);
+        if (!mdok) {
+            psWarning("Unable to find header keyword %s when parsing mask", namekey);
+            continue;
+        }
+
+        // XXX validate that bit is a 2^n value?
+
+        psMetadataItem *item = psMetadataLookup(fpa->masks, name); // Item in recipe with current value
+        if (item) {
+            psAssert(item->type == PS_TYPE_MASK, "Mask entry %s is not of a mask type (%x)",
+                     name, item->type);
+            if (item->data.PS_TYPE_MASK_DATA != bit) {
+                psWarning("New mask entry %s doesn't match previously loaded entry: %x vs %x",
+                          name, bit, item->data.PS_TYPE_MASK_DATA);
+            }
+        } else {
+            psMetadataAddU8(fpa->masks, PS_LIST_TAIL, name, 0, NULL, bit);
+        }
+    }
+
+    // Now copy everything else from the recipe
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(recipe, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type != PS_TYPE_MASK) {
+            psWarning("Recipe mask entry %s is not of a mask type (%x)", item->name, item->type);
+            continue;
+        }
+        if (!psMetadataLookup(fpa->masks, item->name)) {
+            psMetadataAddU8(fpa->masks, PS_LIST_TAIL, item->name, 0, item->comment,
+                            item->data.PS_TYPE_MASK_DATA);
+        }
+    }
+    psFree(iter);
+
+    return true;
+}
+
+
+bool pmFPAMaskWriteHeader(psMetadata *header, const pmFPA *fpa)
+{
     PS_ASSERT_METADATA_NON_NULL(header, false);
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
 
     maskRemoveHeader(header, "MSKNAM");
@@ -175,4 +293,159 @@
     }
 
+    char namekey[80], valuekey[80];     // Mask name and mask value header keywords
+    int numMask = 0;                    // Number of mask entries
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(fpa->masks, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type != PS_TYPE_MASK) {
+            psWarning("mask recipe entry %s is not of a mask type (%x)", item->name, item->type);
+            continue;
+        }
+
+        snprintf(namekey,  64, "MSKNAM%02d", numMask);
+        snprintf(valuekey, 64, "MSKVAL%02d", numMask);
+
+        psMetadataAddStr(header, PS_LIST_TAIL, namekey, 0, "Bitmask bit name", item->name);
+        psMetadataAddU8(header, PS_LIST_TAIL, valuekey, 0, "Bitmask bit value", item->data.PS_TYPE_MASK_DATA);
+        numMask++;
+    }
+    psFree(iter);
+
+    return psMetadataAddS32(header, PS_LIST_TAIL, "MSKNUM", 0, "Number of named mask entries", numMask);
+}
+
+#endif
+
+
+psMaskType pmConfigMaskGet(const char *masks, const pmConfig *config)
+{
+    psAssert(config, "Require configuration");
+    PS_ASSERT_STRING_NON_EMPTY(masks, 0);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return 0;
+    }
+    return maskGet(recipe, masks);
+}
+
+
+bool pmConfigMaskSet(const pmConfig *config, const char *maskName, psMaskType maskValue)
+{
+    psAssert(config, "Require configuration");
+    PS_ASSERT_STRING_NON_EMPTY(maskName, false);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    return psMetadataAddU8(recipe, PS_LIST_TAIL, maskName, PS_META_REPLACE, NULL, maskValue);
+}
+
+
+// replace the named masks in the recipe with values in the header:
+// replace only the names in the header in the recipe
+bool pmConfigMaskReadHeader(pmConfig *config, const psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    bool status = false;
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    // MASK.VALUE and MARK.VALUE aren't usually set in the recipe, but may be set in the header: create fake
+    // versions so that it won't complain later
+    if (!psMetadataLookup(recipe, "MASK.VALUE")) {
+        psMetadataAddU8(recipe, PS_LIST_TAIL, "MASK.VALUE", 0, "Bits to mask", 0);
+    }
+    if (!psMetadataLookup(recipe, "MARK.VALUE")) {
+        psMetadataAddU8(recipe, PS_LIST_TAIL, "MARK.VALUE", 0, "Bits for marking", 0);
+    }
+
+    int nMask = psMetadataLookupS32(&status, header, "MSKNUM");
+    if (!status) {
+        if (psMetadataLookupBool(&status, config->camera, "MASK.FORCE")) {
+            psWarning("No mask values in header.  Assuming MASKS recipe is accurate because of MASK.FORCE");
+            return true;
+        }
+        psError(PS_ERR_UNKNOWN, true, "Unable to find MSKNUM in header.");
+        return false;
+    }
+
+    char namekey[80];                   // Keyword name for symbolic name of mask entry
+    char valuekey[80];                  // Keyword name for value of mask entry
+    for (int i = 0; i < nMask; i++) {
+        snprintf(namekey,  64, "MSKNAM%02d", i);
+        snprintf(valuekey, 64, "MSKVAL%02d", i);
+
+        char *name = psMetadataLookupStr(&status, header, namekey);
+        if (!status || !name) {
+            psWarning("Unable to find header keyword %s when parsing mask", namekey);
+            continue;
+        }
+        psU8 bit = psMetadataLookupU8(&status, header, valuekey);
+        if (!status) {
+            psWarning("Unable to find header keyword %s when parsing mask", namekey);
+            continue;
+        }
+
+        // XXX validate that bit is a 2^n value?
+
+        psString nameAlready = NULL;    // Name of key with ".ALREADY" added
+        psStringAppend(&nameAlready, "%s.ALREADY", name);
+        bool already = psMetadataLookupBool(&status, recipe, nameAlready); // Already read this one?
+
+        psMetadataItem *item = psMetadataLookup(recipe, name); // Item in recipe with current value
+        if (item && item->type != PS_TYPE_MASK) {
+            psWarning("Mask recipe entry is not of a mask type (%x)", item->type);
+            item->type = PS_TYPE_MASK;
+        }
+
+        if (already) {
+            if (item && item->data.U8 != bit) {
+                psWarning("New mask recipe entry doesn't match previously loaded entry: %x vs %x",
+                          bit, item->data.U8);
+            }
+        } else {
+            psMetadataAddBool(recipe, PS_LIST_TAIL, nameAlready, 0, "Already read this mask value", true);
+        }
+
+        if (!item) {
+            psWarning("Mask recipe entry %s not in recipe\n", name);
+            psMetadataAddU8(recipe, PS_LIST_TAIL, name, 0, "Bitmask bit value", bit);
+        } else {
+            item->data.U8 = bit;
+        }
+
+        psFree(nameAlready);
+    }
+
+
+    return true;
+}
+
+
+
+// write the named mask bits to the header
+bool pmConfigMaskWriteHeader(const pmConfig *config, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    maskRemoveHeader(header, "MSKNAM");
+    maskRemoveHeader(header, "MSKVAL");
+    if (psMetadataLookup(header, "MSKNUM")) {
+        psMetadataRemoveKey(header, "MSKNUM");
+    }
+
     char namekey[80];
     char valuekey[80];
@@ -211,114 +484,15 @@
 }
 
-// examine named mask values in mask recipe and set the bits for maskValue and markValue
-// this function sets an appropriate value for the following required named mask concepts:
-// FLAT (used to mark out-of-range corrections in the flat-fielding)
-// BLANK (used to mark non-existent pixels)
-// SAT (used to mark pixels with values out-of-range on the high end)
-// BAD (used to mark pixels with values out-of-range on the low end)
-// If there is no explicit value for the above, the 'DETECTOR' and 'RANGE' bits are used
-// If these latter do not exist, the value 0x01 is used.
-// The values actually used for these names are written back to the config file
+
 bool pmConfigMaskSetBits(psMaskType *outMaskValue, psMaskType *outMarkValue, const pmConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    psMaskType maskValue = 0;
-
-    // mask for generic detector defect
-    psMaskType detectorMask = pmConfigMaskGet("DETECTOR", config);
-    maskValue |= detectorMask;
-
-    // mask for dark structures
-    psMaskType darkMask = pmConfigMaskGet("DARK", config);
-    maskValue |= darkMask;
-
-    // mask for non-linear flat regions (default to DETECTOR if not defined)
-    psMaskType flatMask = pmConfigMaskGet("FLAT", config);
-    if (!flatMask) {
-        flatMask = detectorMask;
-        pmConfigMaskSet (config, "FLAT", flatMask);
-    }
-    if (!flatMask) {
-        flatMask = 0x01;
-        pmConfigMaskSet (config, "FLAT", flatMask);
-    }
-    maskValue |= flatMask;
-
-    // mask for non-existent data  (default to DETECTOR if not defined)
-    psMaskType blankMask = pmConfigMaskGet("BLANK", config);
-    if (!blankMask) {
-        blankMask = detectorMask;
-        pmConfigMaskSet (config, "BLANK", blankMask);
-    }
-    if (!blankMask) {
-        blankMask = 0x01;
-        pmConfigMaskSet (config, "BLANK", blankMask);
-    }
-    maskValue |= blankMask;
-
-    // mask for generic data range errors
-    psMaskType rangeMask = pmConfigMaskGet("RANGE", config);
-    maskValue |= rangeMask;
-
-    // mask for saturated data  (default to RANGE if not defined)
-    psMaskType satMask = pmConfigMaskGet("SAT", config);
-    if (!satMask) {
-        satMask = rangeMask;
-        pmConfigMaskSet (config, "SAT", satMask);
-    }
-    if (!satMask) {
-        satMask = 0x01;
-        pmConfigMaskSet (config, "SAT", satMask);
-    }
-    maskValue |= satMask;
-
-    // mask for below-range data  (default to RANGE if not defined)
-    psMaskType badMask = pmConfigMaskGet("BAD", config);
-    if (!badMask) {
-        badMask = rangeMask;
-        pmConfigMaskSet (config, "BAD", badMask);
-    }
-    if (!badMask) {
-        badMask = 0x01;
-        pmConfigMaskSet (config, "BAD", badMask);
-    }
-    maskValue |= badMask;
-
-    // XXX not sure what to do with these
-    psMaskType crMask = pmConfigMaskGet("CR", config);
-    maskValue |= crMask;
-
-    psMaskType ghostMask = pmConfigMaskGet("GHOST", config);
-    maskValue |= ghostMask;
-
-    // search for an unset bit to use for MARK:
-    psMaskType markValue = 0x80;
-
-    int nBits = sizeof(psMaskType) * 8;
-    for (int i = 0; !markValue && (i < nBits); i++) {
-        if (maskValue & markValue) {
-            markValue >>= 1;
-        } else {
-            markValue = markValue;
-        }
-    }
-    if (!markValue) {
-        psError (PS_ERR_UNKNOWN, true, "Unable to define the MARK bit mask: all bits taken!");
-        return false;
-    }
-
-
-    // update the config table
-    pmConfigMaskSet(config, "MASK.VALUE", maskValue);
-    pmConfigMaskSet(config, "MARK.VALUE", markValue);
-
-    if (outMaskValue) {
-        *outMaskValue = maskValue;
-    }
-    if (outMarkValue) {
-        *outMarkValue = markValue;
-    }
-
-    return true;
-}
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    return maskSetValues(outMaskValue, outMarkValue, recipe);
+}
