From 999d05f695d0e579fa1bb1bc0c4e7af63d671a42 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Wed, 27 Oct 2021 13:38:59 +0200
Subject: [PATCH] sane.ds: Convert the SANE option descriptor to a private
 structure.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
---
 dlls/sane.ds/capability.c |  25 +--
 dlls/sane.ds/options.c    | 287 ++++++++++++++++++----------
 dlls/sane.ds/sane_i.h     |  22 ++-
 dlls/sane.ds/ui.c         | 380 +++++++++++++++++---------------------
 4 files changed, 379 insertions(+), 335 deletions(-)

diff --git a/dlls/sane.ds/capability.c b/dlls/sane.ds/capability.c
index 93ef261a244..56ac3c327d9 100644
--- a/dlls/sane.ds/capability.c
+++ b/dlls/sane.ds/capability.c
@@ -293,19 +293,6 @@ static BOOL pixeltype_to_sane_mode(TW_UINT16 pixeltype, SANE_String mode, int le
     strcpy(mode, m);
     return TRUE;
 }
-static BOOL sane_mode_to_pixeltype(SANE_String_Const mode, TW_UINT16 *pixeltype)
-{
-    if (strcmp(mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
-        *pixeltype = TWPT_BW;
-    else if (memcmp(mode, SANE_VALUE_SCAN_MODE_GRAY, strlen(SANE_VALUE_SCAN_MODE_GRAY)) == 0)
-        *pixeltype = TWPT_GRAY;
-    else if (strcmp(mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
-        *pixeltype = TWPT_RGB;
-    else
-        return FALSE;
-
-    return TRUE;
-}
 
 /* ICAP_PIXELTYPE */
 static TW_UINT16 SANE_ICAPPixelType (pTW_CAPABILITY pCapability, TW_UINT16 action)
@@ -315,22 +302,18 @@ static TW_UINT16 SANE_ICAPPixelType (pTW_CAPABILITY pCapability, TW_UINT16 actio
     int possible_value_count;
     TW_UINT32 val;
     BOOL reload = FALSE;
-    const char * const *choices;
-    char current_mode[64];
     TW_UINT16 current_pixeltype = TWPT_BW;
     SANE_Char mode[64];
 
     TRACE("ICAP_PIXELTYPE\n");
 
-    twCC = sane_option_probe_mode(&choices, current_mode, sizeof(current_mode));
+    twCC = sane_option_probe_mode(&current_pixeltype, possible_values, &possible_value_count);
     if (twCC != TWCC_SUCCESS)
     {
         ERR("Unable to retrieve mode from sane, ICAP_PIXELTYPE unsupported\n");
         return twCC;
     }
 
-    sane_mode_to_pixeltype(current_mode, &current_pixeltype);
-
     /* Sane does not support a concept of a default mode, so we simply cache
      *   the first mode we find */
     if (! activeDS.PixelTypeSet)
@@ -347,12 +330,6 @@ static TW_UINT16 SANE_ICAPPixelType (pTW_CAPABILITY pCapability, TW_UINT16 actio
             break;
 
         case MSG_GET:
-            for (possible_value_count = 0; choices && *choices && possible_value_count < 3; choices++)
-            {
-                TW_UINT16 pix;
-                if (sane_mode_to_pixeltype(*choices, &pix))
-                    possible_values[possible_value_count++] = pix;
-            }
             twCC = msg_get_enum(pCapability, possible_values, possible_value_count,
                     TWTY_UINT16, current_pixeltype, activeDS.defaultPixelType);
             break;
diff --git a/dlls/sane.ds/options.c b/dlls/sane.ds/options.c
index b164ca17883..f4ddec2a846 100644
--- a/dlls/sane.ds/options.c
+++ b/dlls/sane.ds/options.c
@@ -18,8 +18,13 @@
 
 #include "config.h"
 
+#include <stdarg.h>
 #include <stdlib.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
 #include "sane_i.h"
+#include "wine/unicode.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(twain);
@@ -51,6 +56,89 @@ static TW_UINT16 sane_status_to_twcc(SANE_Status rc)
     }
 }
 
+static int map_type( SANE_Value_Type type )
+{
+    switch (type)
+    {
+    case SANE_TYPE_BOOL: return TYPE_BOOL;
+    case SANE_TYPE_INT: return TYPE_INT;
+    case SANE_TYPE_FIXED: return TYPE_FIXED;
+    case SANE_TYPE_STRING: return TYPE_STRING;
+    case SANE_TYPE_BUTTON: return TYPE_BUTTON;
+    case SANE_TYPE_GROUP: return TYPE_GROUP;
+    default: return -1;
+    }
+}
+
+static int map_unit( SANE_Unit unit )
+{
+    switch (unit)
+    {
+    case SANE_UNIT_NONE: return UNIT_NONE;
+    case SANE_UNIT_PIXEL: return UNIT_PIXEL;
+    case SANE_UNIT_BIT: return UNIT_BIT;
+    case SANE_UNIT_MM: return UNIT_MM;
+    case SANE_UNIT_DPI: return UNIT_DPI;
+    case SANE_UNIT_PERCENT: return UNIT_PERCENT;
+    case SANE_UNIT_MICROSECOND: return UNIT_MICROSECOND;
+    default: return -1;
+    }
+}
+
+static int map_constraint_type( SANE_Constraint_Type type )
+{
+    switch (type)
+    {
+    case SANE_CONSTRAINT_NONE: return CONSTRAINT_NONE;
+    case SANE_CONSTRAINT_RANGE: return CONSTRAINT_RANGE;
+    case SANE_CONSTRAINT_WORD_LIST: return CONSTRAINT_WORD_LIST;
+    case SANE_CONSTRAINT_STRING_LIST: return CONSTRAINT_STRING_LIST;
+    default: return CONSTRAINT_NONE;
+    }
+}
+
+static void map_descr( struct option_descriptor *descr, const SANE_Option_Descriptor *opt )
+{
+    unsigned int i, size;
+    WCHAR *p;
+
+    descr->type = map_type( opt->type );
+    descr->unit = map_unit( opt->unit );
+    descr->constraint_type = map_constraint_type( opt->constraint_type );
+    descr->size = opt->size;
+    descr->is_active = SANE_OPTION_IS_ACTIVE( opt->cap );
+    if (opt->title)
+        MultiByteToWideChar( CP_UNIXCP, 0, opt->title, -1, descr->title, ARRAY_SIZE(descr->title) );
+    else
+        descr->title[0] = 0;
+    switch (descr->constraint_type)
+    {
+    case CONSTRAINT_RANGE:
+        descr->constraint.range.min = opt->constraint.range->min;
+        descr->constraint.range.max = opt->constraint.range->max;
+        descr->constraint.range.quant = opt->constraint.range->quant;
+        break;
+    case CONSTRAINT_WORD_LIST:
+        size = min( opt->constraint.word_list[0], ARRAY_SIZE(descr->constraint.word_list) - 1 );
+        descr->constraint.word_list[0] = size;
+        for (i = 1; i <= size; i++) descr->constraint.word_list[i] = opt->constraint.word_list[i];
+        break;
+    case CONSTRAINT_STRING_LIST:
+        p = descr->constraint.strings;
+        size = ARRAY_SIZE(descr->constraint.strings) - 1;
+        for (i = 0; size && opt->constraint.string_list[i]; i++)
+        {
+            int len = MultiByteToWideChar( CP_UNIXCP, 0, opt->constraint.string_list[i], -1, p, size );
+            size -= len;
+            p += len;
+        }
+        *p = 0;
+        break;
+    default:
+        break;
+    }
+}
+
 TW_UINT16 sane_option_get_value( int optno, void *val )
 {
     return sane_status_to_twcc( sane_control_option( activeDS.deviceHandle, optno,
@@ -67,88 +155,76 @@ TW_UINT16 sane_option_set_value( int optno, void *val, BOOL *reload )
     return rc;
 }
 
-static TW_UINT16 sane_find_option(const char *option_name,
-        const SANE_Option_Descriptor **opt_p, int *optno, SANE_Value_Type type)
+TW_UINT16 sane_option_get_descriptor( struct option_descriptor *descr )
 {
-    TW_UINT16 rc;
-    int optcount;
-    const SANE_Option_Descriptor *opt;
-    int i;
+    const SANE_Option_Descriptor *opt = sane_get_option_descriptor( activeDS.deviceHandle, descr->optno );
 
-    /* Debian, in 32_net_backend_standard_fix.dpatch,
-     *  forces a frontend (that's us) to reload options
-     *  manually by invoking get_option_descriptor. */
-    opt = sane_get_option_descriptor(activeDS.deviceHandle, 0);
-    if (! opt)
-        return TWCC_BUMMER;
+    if (!opt) return TWCC_CAPUNSUPPORTED;
+    map_descr( descr, opt );
+    return TWCC_SUCCESS;
+}
 
-    rc = sane_option_get_value( 0, &optcount );
-    if (rc != TWCC_SUCCESS) return rc;
+static TW_UINT16 sane_find_option( const char *name, int type, struct option_descriptor *descr )
+{
+    const SANE_Option_Descriptor *opt;
+    int i;
 
-    for (i = 1; i < optcount; i++)
+    for (i = 1; (opt = sane_get_option_descriptor( activeDS.deviceHandle, i )) != NULL; i++)
     {
-        opt = sane_get_option_descriptor(activeDS.deviceHandle, i);
-        if (opt && (opt->name && strcmp(opt->name, option_name) == 0) &&
-               opt->type == type)
-        {
-            *opt_p = opt;
-            *optno = i;
-            return TWCC_SUCCESS;
-        }
+        if (type != map_type( opt->type )) continue;
+        if (strcmp( name, opt->name )) continue;
+        descr->optno = i;
+        map_descr( descr, opt );
+        return TWCC_SUCCESS;
     }
     return TWCC_CAPUNSUPPORTED;
 }
 
 TW_UINT16 sane_option_get_int(const char *option_name, int *val)
 {
-    int optno;
-    const SANE_Option_Descriptor *opt;
-    TW_UINT16 rc = sane_find_option(option_name, &opt, &optno, SANE_TYPE_INT);
+    struct option_descriptor opt;
+    TW_UINT16 rc = sane_find_option(option_name, TYPE_INT, &opt);
 
-    if (rc == TWCC_SUCCESS) rc = sane_option_get_value( optno, val );
+    if (rc == TWCC_SUCCESS) rc = sane_option_get_value( opt.optno, val );
     return rc;
 }
 
 TW_UINT16 sane_option_set_int(const char *option_name, int val, BOOL *needs_reload )
 {
-    int optno;
-    const SANE_Option_Descriptor *opt;
-    TW_UINT16 rc = sane_find_option(option_name, &opt, &optno, SANE_TYPE_INT);
+    struct option_descriptor opt;
+    TW_UINT16 rc = sane_find_option(option_name, TYPE_INT, &opt);
 
-    if (rc == TWCC_SUCCESS) rc = sane_option_set_value( optno, &val, needs_reload );
+    if (rc == TWCC_SUCCESS) rc = sane_option_set_value( opt.optno, &val, needs_reload );
     return rc;
 }
 
 TW_UINT16 sane_option_get_bool(const char *option_name, int *val)
 {
-    int optno;
-    const SANE_Option_Descriptor *opt;
-    TW_UINT16 rc = sane_find_option(option_name, &opt, &optno, SANE_TYPE_BOOL);
+    struct option_descriptor opt;
+    TW_UINT16 rc = sane_find_option(option_name, TYPE_BOOL, &opt);
 
-    if (rc == TWCC_SUCCESS) rc = sane_option_get_value( optno, val );
+    if (rc == TWCC_SUCCESS) rc = sane_option_get_value( opt.optno, val );
     return rc;
 }
 
 TW_UINT16 sane_option_set_bool(const char *option_name, int val )
 {
-    int optno;
-    const SANE_Option_Descriptor *opt;
-    TW_UINT16 rc = sane_find_option(option_name, &opt, &optno, SANE_TYPE_BOOL);
+    struct option_descriptor opt;
+    TW_UINT16 rc = sane_find_option(option_name, TYPE_BOOL, &opt);
 
-    if (rc == TWCC_SUCCESS) rc = sane_option_set_value( optno, &val, NULL );
+    if (rc == TWCC_SUCCESS) rc = sane_option_set_value( opt.optno, &val, NULL );
     return rc;
 }
 
 TW_UINT16 sane_option_get_str(const char *option_name, char *val, int len)
 {
-    int optno;
-    const SANE_Option_Descriptor *opt;
-    TW_UINT16 rc = sane_find_option(option_name, &opt, &optno, SANE_TYPE_STRING);
+    struct option_descriptor opt;
+    TW_UINT16 rc = sane_find_option(option_name, TYPE_STRING, &opt);
 
     if (rc == TWCC_SUCCESS)
     {
-        if (opt->size < len)
-            rc = sane_option_get_value( optno, val );
+        if (opt.size < len)
+            rc = sane_option_get_value( opt.optno, val );
         else
             rc = TWCC_BADVALUE;
     }
@@ -158,97 +234,106 @@ TW_UINT16 sane_option_get_str(const char *option_name, char *val, int len)
 /* Important:  SANE has the side effect of overwriting val with the returned value */
 TW_UINT16 sane_option_set_str(const char *option_name, char *val, BOOL *needs_reload)
 {
-    int optno;
-    const SANE_Option_Descriptor *opt;
-    TW_UINT16 rc = sane_find_option(option_name, &opt, &optno, SANE_TYPE_STRING);
+    struct option_descriptor opt;
+    TW_UINT16 rc = sane_find_option(option_name, TYPE_STRING, &opt);
 
-    if (rc == TWCC_SUCCESS) rc = sane_option_set_value( optno, &val, needs_reload );
+    if (rc == TWCC_SUCCESS) rc = sane_option_set_value( opt.optno, &val, needs_reload );
     return rc;
 }
 
 TW_UINT16 sane_option_probe_resolution(const char *option_name, int *minval, int *maxval, int *quant)
 {
-    int optno;
-    const SANE_Option_Descriptor *opt;
-    TW_UINT16 rc = sane_find_option(option_name, &opt, &optno, SANE_TYPE_INT);
+    struct option_descriptor opt;
+    TW_UINT16 rc = sane_find_option(option_name, TYPE_INT, &opt);
 
     if (rc != TWCC_SUCCESS) return rc;
-    if (opt->constraint_type != SANE_CONSTRAINT_RANGE) return TWCC_CAPUNSUPPORTED;
-
-    *minval = opt->constraint.range->min;
-    *maxval = opt->constraint.range->max;
-    *quant = opt->constraint.range->quant;
+    if (opt.constraint_type != CONSTRAINT_RANGE) return TWCC_CAPUNSUPPORTED;
 
+    *minval = opt.constraint.range.min;
+    *maxval = opt.constraint.range.max;
+    *quant  = opt.constraint.range.quant;
     return rc;
 }
 
-TW_UINT16 sane_option_probe_mode(const char * const **choices, char *current, int current_size)
+TW_UINT16 sane_option_probe_mode(TW_UINT16 *current, TW_UINT32 *choices, int *count)
 {
-    int optno;
-    const SANE_Option_Descriptor *opt;
-    TW_UINT16 rc = sane_find_option("mode", &opt, &optno, SANE_TYPE_STRING);
+    WCHAR *p;
+    char buffer[256];
+    struct option_descriptor opt;
+    TW_UINT16 rc = sane_find_option("mode", TYPE_STRING, &opt);
 
     if (rc != TWCC_SUCCESS) return rc;
-    if (choices && opt->constraint_type == SANE_CONSTRAINT_STRING_LIST)
-        *choices = opt->constraint.string_list;
+    if (opt.size > sizeof(buffer)) return TWCC_BADVALUE;
+    rc = sane_option_get_value( opt.optno, buffer );
+    if (rc != TWCC_SUCCESS) return rc;
 
-    if (opt->size < current_size)
-        return sane_option_get_value( optno, current );
-    else
-        return TWCC_BADVALUE;
+    if (!strcmp( buffer, "Lineart" )) *current = TWPT_BW;
+    else if (!strcmp( buffer, "Color" )) *current = TWPT_RGB;
+    else if (!strncmp( buffer, "Gray", 4 )) *current = TWPT_GRAY;
 
+    *count = 0;
+    if (opt.constraint_type == CONSTRAINT_STRING_LIST)
+    {
+        for (p = opt.constraint.strings; *p; p += strlenW(p) + 1)
+        {
+            static const WCHAR lineartW[] = {'L','i','n','e','a','r','t',0};
+            static const WCHAR colorW[] = {'C','o','l','o','r',0};
+            static const WCHAR grayW[] = {'G','r','a','y',0};
+            if (!strcmpW( p, lineartW )) choices[(*count)++] = TWPT_BW;
+            else if (!strcmpW( p, colorW )) choices[(*count)++] = TWPT_RGB;
+            else if (!strncmpW( p, grayW, 4 )) choices[(*count)++] = TWPT_GRAY;
+        }
+    }
+    return rc;
 }
 
 TW_UINT16 sane_option_get_scan_area( int *tlx, int *tly, int *brx, int *bry )
 {
     TW_UINT16 rc;
-    int optno;
-    const SANE_Option_Descriptor *opt;
-
-    if ((rc = sane_find_option( "tl-x", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_option_get_value( optno, tlx )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_find_option( "tl-y", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_option_get_value( optno, tly )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_find_option( "br-x", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_option_get_value( optno, brx )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_find_option( "br-y", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_option_get_value( optno, bry )) != TWCC_SUCCESS) return rc;
-    if (opt->unit != SANE_UNIT_MM) FIXME( "unsupported unit %u\n", opt->unit );
+    struct option_descriptor opt;
+
+    if ((rc = sane_find_option( "tl-x", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_option_get_value( opt.optno, tlx )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_find_option( "tl-y", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_option_get_value( opt.optno, tly )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_find_option( "br-x", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_option_get_value( opt.optno, brx )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_find_option( "br-y", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_option_get_value( opt.optno, bry )) != TWCC_SUCCESS) return rc;
+    if (opt.unit != UNIT_MM) FIXME( "unsupported unit %u\n", opt.unit );
     return rc;
 }
 
 TW_UINT16 sane_option_get_max_scan_area( int *tlx, int *tly, int *brx, int *bry )
 {
     TW_UINT16 rc;
-    int optno;
-    const SANE_Option_Descriptor *opt;
-
-    if ((rc = sane_find_option( "tl-x", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    *tlx = opt->constraint.range->min;
-    if ((rc = sane_find_option( "tl-y", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    *tly = opt->constraint.range->min;
-    if ((rc = sane_find_option( "br-x", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    *brx = opt->constraint.range->max;
-    if ((rc = sane_find_option( "br-y", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    *bry = opt->constraint.range->max;
-    if (opt->unit != SANE_UNIT_MM) FIXME( "unsupported unit %u\n", opt->unit );
+    struct option_descriptor opt;
+
+    if ((rc = sane_find_option( "tl-x", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    *tlx = opt.constraint.range.min;
+    if ((rc = sane_find_option( "tl-y", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    *tly = opt.constraint.range.min;
+    if ((rc = sane_find_option( "br-x", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    *brx = opt.constraint.range.max;
+    if ((rc = sane_find_option( "br-y", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    *bry = opt.constraint.range.max;
+    if (opt.unit != UNIT_MM) FIXME( "unsupported unit %u\n", opt.unit );
     return rc;
 }
 
 TW_UINT16 sane_option_set_scan_area( int tlx, int tly, int brx, int bry, BOOL *reload )
 {
     TW_UINT16 rc;
-    int optno;
-    const SANE_Option_Descriptor *opt;
-
-    if ((rc = sane_find_option( "tl-x", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_option_set_value( optno, &tlx, reload )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_find_option( "tl-y", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_option_set_value( optno, &tly, reload )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_find_option( "br-x", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_option_set_value( optno, &brx, reload )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_find_option( "br-y", &opt, &optno, SANE_TYPE_FIXED )) != TWCC_SUCCESS) return rc;
-    if ((rc = sane_option_set_value( optno, &bry, reload )) != TWCC_SUCCESS) return rc;
+    struct option_descriptor opt;
+
+    if ((rc = sane_find_option( "tl-x", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_option_set_value( opt.optno, &tlx, reload )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_find_option( "tl-y", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_option_set_value( opt.optno, &tly, reload )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_find_option( "br-x", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_option_set_value( opt.optno, &brx, reload )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_find_option( "br-y", TYPE_FIXED, &opt )) != TWCC_SUCCESS) return rc;
+    if ((rc = sane_option_set_value( opt.optno, &bry, reload )) != TWCC_SUCCESS) return rc;
     return rc;
 }
 
diff --git a/dlls/sane.ds/sane_i.h b/dlls/sane.ds/sane_i.h
index 666ebcbd76a..db6032c6ba7 100644
--- a/dlls/sane.ds/sane_i.h
+++ b/dlls/sane.ds/sane_i.h
@@ -47,6 +47,25 @@ struct frame_parameters
     int  depth;
 };
 
+struct option_descriptor
+{
+    int optno;
+    int size;
+    int is_active;
+    enum { TYPE_BOOL, TYPE_INT, TYPE_FIXED, TYPE_STRING, TYPE_BUTTON, TYPE_GROUP } type;
+    enum { UNIT_NONE, UNIT_PIXEL, UNIT_BIT, UNIT_MM, UNIT_DPI, UNIT_PERCENT, UNIT_MICROSECOND } unit;
+    enum { CONSTRAINT_NONE, CONSTRAINT_RANGE, CONSTRAINT_WORD_LIST, CONSTRAINT_STRING_LIST } constraint_type;
+
+    WCHAR title[256];
+
+    union
+    {
+        WCHAR strings[256];
+        int word_list[256];	/* first element is list-length */
+        struct { int min, max, quant; } range;
+    } constraint;
+};
+
 /* internal information about an active data source */
 struct tagActiveDS
 {
@@ -216,12 +235,13 @@ HWND ScanningDialogBox(HWND dialog, LONG progress) DECLSPEC_HIDDEN;
 /* Option functions */
 TW_UINT16 sane_option_get_value( int optno, void *val ) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_set_value( int optno, void *val, BOOL *reload ) DECLSPEC_HIDDEN;
+TW_UINT16 sane_option_get_descriptor( struct option_descriptor *descr ) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_get_int( const char *option_name, int *val ) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_set_int( const char *option_name, int val, BOOL *reload ) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_get_str( const char *option_name, char *val, int len ) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_set_str( const char *option_name, char *val, BOOL *reload ) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_probe_resolution( const char *option_name, int *minval, int *maxval, int *quant) DECLSPEC_HIDDEN;
-TW_UINT16 sane_option_probe_mode(const char * const **choices, char *current, int current_size) DECLSPEC_HIDDEN;
+TW_UINT16 sane_option_probe_mode(TW_UINT16 *current, TW_UINT32 *choices, int *count) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_get_bool( const char *option_name, BOOL *val ) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_set_bool( const char *option_name, BOOL val ) DECLSPEC_HIDDEN;
 TW_UINT16 sane_option_get_scan_area( int *tlx, int *tly, int *brx, int *bry ) DECLSPEC_HIDDEN;
diff --git a/dlls/sane.ds/ui.c b/dlls/sane.ds/ui.c
index 721ad0fac06..343976060dd 100644
--- a/dlls/sane.ds/ui.c
+++ b/dlls/sane.ds/ui.c
@@ -44,13 +44,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(twain);
 static INT_PTR CALLBACK DialogProc (HWND , UINT , WPARAM , LPARAM );
 static INT CALLBACK PropSheetProc(HWND, UINT,LPARAM);
 
-static int create_leading_static(HDC hdc, LPCSTR text,
+static int create_leading_static(HDC hdc, const WCHAR *text,
         LPDLGITEMTEMPLATEW* template_out, int y, int id)
 {
     LPDLGITEMTEMPLATEW tpl =  NULL;
     INT len;
     SIZE size;
-    LPBYTE ptr;
+    WORD *ptr;
     LONG base;
 
     *template_out = NULL;
@@ -60,10 +60,9 @@ static int create_leading_static(HDC hdc, LPCSTR text,
 
     base = GetDialogBaseUnits();
 
-    len = MultiByteToWideChar(CP_ACP,0,text,-1,NULL,0);
-    len *= sizeof(WCHAR);
+    len = strlenW(text) * sizeof(WCHAR);
     len += sizeof(DLGITEMTEMPLATE);
-    len += 3*sizeof(WORD);
+    len += 4*sizeof(WORD);
 
     tpl = HeapAlloc(GetProcessHeap(),0,len);
     tpl->style=WS_VISIBLE;
@@ -72,28 +71,27 @@ static int create_leading_static(HDC hdc, LPCSTR text,
     tpl->y = y;
     tpl->id = ID_BASE;
 
-    GetTextExtentPoint32A(hdc,text,lstrlenA(text),&size);
+    GetTextExtentPoint32W(hdc,text,lstrlenW(text),&size);
 
     tpl->cx =  MulDiv(size.cx,4,LOWORD(base));
     tpl->cy =  MulDiv(size.cy,8,HIWORD(base)) * 2;
-    ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
-    *(LPWORD)ptr = 0xffff;
-    ptr += sizeof(WORD);
-    *(LPWORD)ptr = 0x0082;
-    ptr += sizeof(WORD);
-    ptr += MultiByteToWideChar(CP_ACP,0,text,-1,(LPWSTR)ptr,len) * sizeof(WCHAR);
-    *(LPWORD)ptr = 0x0000;
+    ptr = (WORD *)(tpl + 1);
+    *ptr++ = 0xffff;
+    *ptr++ = 0x0082;
+    strcpyW( ptr, text );
+    ptr += strlenW(ptr) + 1;
+    *ptr = 0;
 
     *template_out = tpl;
     return len;
 }
 
 static int create_trailing_edit(HDC hdc, LPDLGITEMTEMPLATEW* template_out, int id,
-        int y, LPCSTR text,BOOL is_int)
+        int y, const WCHAR *text,BOOL is_int)
 {
     LPDLGITEMTEMPLATEW tpl =  NULL;
     INT len;
-    LPBYTE ptr;
+    WORD *ptr;
     SIZE size;
     LONG base;
     static const char int_base[] = "0000 xxx";
@@ -101,10 +99,9 @@ static int create_trailing_edit(HDC hdc, LPDLGITEMTEMPLATEW* template_out, int i
 
     base = GetDialogBaseUnits();
 
-    len = MultiByteToWideChar(CP_ACP,0,text,-1,NULL,0);
-    len *= sizeof(WCHAR);
+    len = strlenW(text) * sizeof(WCHAR);
     len += sizeof(DLGITEMTEMPLATE);
-    len += 3*sizeof(WORD);
+    len += 4*sizeof(WORD);
 
     tpl = HeapAlloc(GetProcessHeap(),0,len);
     tpl->style=WS_VISIBLE|ES_READONLY|WS_BORDER;
@@ -121,33 +118,32 @@ static int create_trailing_edit(HDC hdc, LPDLGITEMTEMPLATEW* template_out, int i
     tpl->cx =  MulDiv(size.cx*2,4,LOWORD(base));
     tpl->cy =  MulDiv(size.cy,8,HIWORD(base)) * 2;
 
-    ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
-    *(LPWORD)ptr = 0xffff;
-    ptr += sizeof(WORD);
-    *(LPWORD)ptr = 0x0081;
-    ptr += sizeof(WORD);
-    ptr += MultiByteToWideChar(CP_ACP,0,text,-1,(LPWSTR)ptr,len) * sizeof(WCHAR);
-    *(LPWORD)ptr = 0x0000;
+    ptr = (WORD *)(tpl + 1);
+    *ptr++ = 0xffff;
+    *ptr++ = 0x0081;
+    strcpyW( ptr, text );
+    ptr += strlenW(ptr) + 1;
+    *ptr = 0;
 
     *template_out = tpl;
     return len;
 }
 
 
-static int create_item(HDC hdc, const SANE_Option_Descriptor *opt,
+static int create_item(HDC hdc, const struct option_descriptor *opt,
         INT id, LPDLGITEMTEMPLATEW *template_out, int y, int *cx, int* count)
 {
     LPDLGITEMTEMPLATEW tpl = NULL,rc = NULL;
     WORD class = 0xffff;
     DWORD styles = WS_VISIBLE;
-    LPBYTE ptr = NULL;
+    WORD *ptr = NULL;
     LPDLGITEMTEMPLATEW lead_static = NULL;
     LPDLGITEMTEMPLATEW trail_edit = NULL;
     DWORD leading_len = 0;
     DWORD trail_len = 0;
     DWORD local_len = 0;
-    LPCSTR title = NULL;
-    CHAR buffer[255];
+    const WCHAR *title = NULL;
+    WCHAR buffer[255];
     int padding = 0;
     int padding2 = 0;
     int base_x = 0;
@@ -159,122 +155,110 @@ static int create_item(HDC hdc, const SANE_Option_Descriptor *opt,
     base = GetDialogBaseUnits();
     base_x = MulDiv(size.cx,4,LOWORD(base));
 
-    if (opt->type == SANE_TYPE_BOOL)
+    switch (opt->type)
     {
+    case TYPE_BOOL:
         class = 0x0080; /* Button */
         styles |= BS_AUTOCHECKBOX;
-        local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
-        local_len *= sizeof(WCHAR);
         title = opt->title;
-    }
-    else if (opt->type == SANE_TYPE_INT)
+        break;
+    case TYPE_INT:
     {
         int i;
-
+        static const WCHAR fmtW[] = {'%','i',0};
         sane_option_get_value( id - ID_BASE, &i );
-        sprintf(buffer,"%i",i);
+        sprintfW(buffer,fmtW,i);
 
-        if (opt->constraint_type == SANE_CONSTRAINT_NONE)
+        switch (opt->constraint_type)
         {
+        case CONSTRAINT_NONE:
             class = 0x0081; /* Edit*/
             styles |= ES_NUMBER;
             title = buffer;
-            local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
-            local_len *= sizeof(WCHAR);
-        }
-        else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
-        {
+            break;
+        case CONSTRAINT_RANGE:
             class = 0x0084; /* scroll */
             ctl_cx = 10 * base_x;
             trail_len += create_trailing_edit(hdc, &trail_edit, id +
                     ID_EDIT_BASE, y,buffer,TRUE);
-        }
-        else
-        {
+            break;
+        default:
             class= 0x0085; /* Combo */
             ctl_cx = 10 * base_x;
             styles |= CBS_DROPDOWNLIST;
+            break;
         }
-        leading_len += create_leading_static(hdc, opt->title, &lead_static, y, 
-                id+ID_STATIC_BASE);
+        leading_len += create_leading_static(hdc, opt->title, &lead_static, y, id+ID_STATIC_BASE);
+        break;
     }
-    else if (opt->type == SANE_TYPE_FIXED)
+    case TYPE_FIXED:
     {
         SANE_Fixed *i;
         double dd;
+        static const WCHAR fmtW[] = {'%','f',0};
 
         i = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
 
         sane_option_get_value( id - ID_BASE, i );
 
         dd = SANE_UNFIX(*i);
-        sprintf(buffer,"%f",dd);
+        sprintfW(buffer,fmtW,dd);
         HeapFree(GetProcessHeap(),0,i);
 
-        if (opt->constraint_type == SANE_CONSTRAINT_NONE)
+        switch (opt->constraint_type)
         {
+        case CONSTRAINT_NONE:
             class = 0x0081; /* Edit */
             title = buffer;
-            local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
-            local_len *= sizeof(WCHAR);
-        }
-        else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
-        {
+            break;
+        case CONSTRAINT_RANGE:
             class= 0x0084; /* scroll */
             ctl_cx = 10 * base_x;
-            trail_len += create_trailing_edit(hdc, &trail_edit, id +
-                    ID_EDIT_BASE, y,buffer,FALSE);
-        }
-        else
-        {
+            trail_len += create_trailing_edit(hdc, &trail_edit, id + ID_EDIT_BASE, y,buffer,FALSE);
+            break;
+        default:
             class= 0x0085; /* Combo */
             ctl_cx = 10 * base_x;
             styles |= CBS_DROPDOWNLIST;
+            break;
         }
-        leading_len += create_leading_static(hdc, opt->title, &lead_static, y,
-                id+ID_STATIC_BASE);
+        leading_len += create_leading_static(hdc, opt->title, &lead_static, y, id+ID_STATIC_BASE);
+        break;
     }
-    else if (opt->type == SANE_TYPE_STRING)
+    case TYPE_STRING:
     {
-        if (opt->constraint_type == SANE_CONSTRAINT_NONE)
+        char str[256];
+        switch (opt->constraint_type)
         {
+        case CONSTRAINT_NONE:
             class = 0x0081; /* Edit*/
-        }
-        else
-        {
+            break;
+        default:
             class= 0x0085; /* Combo */
             ctl_cx = opt->size * base_x;
             styles |= CBS_DROPDOWNLIST;
+            break;
         }
-        leading_len += create_leading_static(hdc, opt->title, &lead_static, y,
-                id+ID_STATIC_BASE);
-        sane_option_get_value( id - ID_BASE, buffer );
-
+        leading_len += create_leading_static(hdc, opt->title, &lead_static, y, id+ID_STATIC_BASE);
+        sane_option_get_value( id - ID_BASE, str );
+        MultiByteToWideChar( CP_UNIXCP, 0, str, -1, buffer, ARRAY_SIZE(buffer) );
         title = buffer;
-        local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
-        local_len *= sizeof(WCHAR);
+        break;
     }
-    else if (opt->type == SANE_TYPE_BUTTON)
-    {
+    case TYPE_BUTTON:
         class = 0x0080; /* Button */
-        local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
-        local_len *= sizeof(WCHAR);
         title = opt->title;
-    }
-    else if (opt->type == SANE_TYPE_GROUP)
-    {
+        break;
+    case TYPE_GROUP:
         class = 0x0080; /* Button */
         styles |= BS_GROUPBOX;
-        local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
-        local_len *= sizeof(WCHAR);
         title = opt->title;
+        break;
     }
 
     local_len += sizeof(DLGITEMTEMPLATE);
-    if (title)
-        local_len += 3*sizeof(WORD);
-    else
-        local_len += 4*sizeof(WORD);
+    if (title) local_len += strlenW(title) * sizeof(WCHAR);
+    local_len += 4*sizeof(WORD);
 
     if (lead_static)
     {
@@ -289,7 +273,7 @@ static int create_item(HDC hdc, const SANE_Option_Descriptor *opt,
     tpl->dwExtendedStyle = 0;
     if (lead_static)
         tpl->x = lead_static->x + lead_static->cx + 1;
-    else if (opt->type == SANE_TYPE_GROUP)
+    else if (opt->type == TYPE_GROUP)
         tpl->x = 2;
     else
         tpl->x = 4;
@@ -298,7 +282,7 @@ static int create_item(HDC hdc, const SANE_Option_Descriptor *opt,
 
     if (title)
     {
-        GetTextExtentPoint32A(hdc,title,lstrlenA(title),&size);
+        GetTextExtentPoint32W(hdc,title,lstrlenW(title),&size);
         tpl->cx = size.cx;
         tpl->cy = size.cy;
     }
@@ -314,23 +298,16 @@ static int create_item(HDC hdc, const SANE_Option_Descriptor *opt,
 
         tpl->cx = ctl_cx;
     }
-    ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
-    *(LPWORD)ptr = 0xffff;
-    ptr += sizeof(WORD);
-    *(LPWORD)ptr = class;
-    ptr += sizeof(WORD);
+    ptr = (WORD *)(tpl + 1);
+    *ptr++ = 0xffff;
+    *ptr++ = class;
     if (title)
     {
-        ptr += MultiByteToWideChar(CP_ACP,0,title,-1,(LPWSTR)ptr,local_len) * sizeof(WCHAR);
-    }
-    else
-    {
-        *(LPWORD)ptr = 0x0000;
-        ptr += sizeof(WORD);
+        strcpyW( ptr, title );
+        ptr += strlenW(ptr);
     }
-
-    *((LPWORD)ptr) = 0x0000;
-    ptr += sizeof(WORD);
+    *ptr++ = 0;
+    *ptr = 0;
 
     if (trail_edit)
     {
@@ -378,17 +355,16 @@ static LPDLGTEMPLATEW create_options_page(HDC hdc, int *from_index,
     for (i = *from_index; i < optcount; i++)
     {
         LPDLGITEMTEMPLATEW item_tpl = NULL;
-        const SANE_Option_Descriptor *opt;
+        struct option_descriptor opt;
         int len;
         int padding = 0;
         int x;
         int count;
         int hold_for_group = 0;
 
-        opt = sane_get_option_descriptor(activeDS.deviceHandle, i);
-        if (!opt)
-            continue;
-        if (opt->type == SANE_TYPE_GROUP && split_tabs)
+        opt.optno = i;
+        if (sane_option_get_descriptor( &opt )) continue;
+        if (opt.type == TYPE_GROUP && split_tabs)
         {
             if (control_len > 0)
             {
@@ -401,10 +377,10 @@ static LPDLGTEMPLATEW create_options_page(HDC hdc, int *from_index,
                 return NULL;
             }
         }
-        if (!SANE_OPTION_IS_ACTIVE (opt->cap))
+        if (!opt.is_active)
             continue;
 
-        len = create_item(hdc, opt, ID_BASE + i, &item_tpl, y, &x, &count);
+        len = create_item(hdc, &opt, ID_BASE + i, &item_tpl, y, &x, &count);
 
         control_count += count;
 
@@ -445,7 +421,7 @@ static LPDLGTEMPLATEW create_options_page(HDC hdc, int *from_index,
             }
         }
 
-        if (opt->type == SANE_TYPE_GROUP)
+        if (opt.type == TYPE_GROUP)
         {
             if (group_offset == -1)
             {
@@ -532,20 +508,17 @@ BOOL DoScannerUI(void)
 
     while (index < optcount)
     {
-        const SANE_Option_Descriptor *opt;
+        struct option_descriptor opt;
+
         psp[page_count].u.pResource = create_options_page(hdc, &index,
                 optcount, TRUE);
-        opt = sane_get_option_descriptor(activeDS.deviceHandle, index);
+        opt.optno = index;
+        sane_option_get_descriptor( &opt );
 
-        if (opt->type == SANE_TYPE_GROUP)
+        if (opt.type == TYPE_GROUP)
         {
-            LPWSTR title = NULL;
-            INT len;
-
-            len = MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
-            title = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
-            MultiByteToWideChar(CP_ACP,0,opt->title,-1,title,len);
-
+            LPWSTR title = HeapAlloc(GetProcessHeap(),0,(strlenW(opt.title) + 1) * sizeof(WCHAR));
+            strcpyW( title, opt.title );
             psp[page_count].pszTitle = title;
         }
 
@@ -596,31 +569,33 @@ BOOL DoScannerUI(void)
         return FALSE;
 }
 
-static void UpdateRelevantEdit(HWND hwnd, const SANE_Option_Descriptor *opt, 
-        int index, int position)
+static void UpdateRelevantEdit(HWND hwnd, const struct option_descriptor *opt, int position)
 {
     WCHAR buffer[244];
     HWND edit_w;
     int len;
 
-    if (opt->type == SANE_TYPE_INT)
+    switch (opt->type)
+    {
+    case TYPE_INT:
     {
         static const WCHAR formatW[] = {'%','i',0};
         INT si;
 
-        if (opt->constraint.range->quant)
-            si = position * opt->constraint.range->quant;
+        if (opt->constraint.range.quant)
+            si = position * opt->constraint.range.quant;
         else
             si = position;
 
         len = sprintfW( buffer, formatW, si );
+        break;
     }
-    else if  (opt->type == SANE_TYPE_FIXED)
+    case TYPE_FIXED:
     {
         static const WCHAR formatW[] = {'%','f',0};
         double s_quant, dd;
 
-        s_quant = SANE_UNFIX(opt->constraint.range->quant);
+        s_quant = SANE_UNFIX(opt->constraint.range.quant);
 
         if (s_quant)
             dd = position * s_quant;
@@ -628,39 +603,44 @@ static void UpdateRelevantEdit(HWND hwnd, const SANE_Option_Descriptor *opt,
             dd = position * 0.01;
 
         len = sprintfW( buffer, formatW, dd );
+        break;
+    }
+    default:
+        return;
     }
-    else return;
 
     buffer[len++] = ' ';
     LoadStringW( SANE_instance, opt->unit, buffer + len, ARRAY_SIZE( buffer ) - len );
 
-    edit_w = GetDlgItem(hwnd,index+ID_BASE+ID_EDIT_BASE);
+    edit_w = GetDlgItem(hwnd,opt->optno + ID_BASE + ID_EDIT_BASE);
     if (edit_w) SetWindowTextW(edit_w,buffer);
 
 }
 
-static BOOL UpdateSaneScrollOption(
-        const SANE_Option_Descriptor *opt, int index, DWORD position)
+static BOOL UpdateSaneScrollOption(const struct option_descriptor *opt, DWORD position)
 {
     BOOL result = FALSE;
 
-    if (opt->type == SANE_TYPE_INT)
+    switch (opt->type)
+    {
+    case TYPE_INT:
     {
         int si;
 
-        if (opt->constraint.range->quant)
-            si = position * opt->constraint.range->quant;
+        if (opt->constraint.range.quant)
+            si = position * opt->constraint.range.quant;
         else
             si = position;
 
-        sane_option_set_value( index, &si, &result );
+        sane_option_set_value( opt->optno, &si, &result );
+        break;
     }
-    else if  (opt->type == SANE_TYPE_FIXED)
+    case TYPE_FIXED:
     {
         double s_quant, dd;
         SANE_Fixed *sf;
 
-        s_quant = SANE_UNFIX(opt->constraint.range->quant);
+        s_quant = SANE_UNFIX(opt->constraint.range.quant);
 
         if (s_quant)
             dd = position * s_quant;
@@ -671,9 +651,13 @@ static BOOL UpdateSaneScrollOption(
 
         *sf = SANE_FIX(dd);
 
-        sane_option_set_value( index, sf, &result );
+        sane_option_set_value( opt->optno, sf, &result );
 
         HeapFree(GetProcessHeap(),0,sf);
+        break;
+    }
+    default:
+        break;
     }
 
     return result;
@@ -695,39 +679,32 @@ static INT_PTR InitializeDialog(HWND hwnd)
 
     for ( i = 1; i < optcount; i++)
     {
-        const SANE_Option_Descriptor *opt;
+        struct option_descriptor opt;
 
         control = GetDlgItem(hwnd,i+ID_BASE);
 
         if (!control)
             continue;
 
-        opt = sane_get_option_descriptor(activeDS.deviceHandle, i);
+        opt.optno = i;
+        sane_option_get_descriptor( &opt );
 
-        TRACE("%i %s %i %i\n",i,opt->title,opt->type,opt->constraint_type);
-        
-        if (!SANE_OPTION_IS_ACTIVE(opt->cap))
-            EnableWindow(control,FALSE);
-        else
-            EnableWindow(control,TRUE);
+        TRACE("%i %s %i %i\n",i,debugstr_w(opt.title),opt.type,opt.constraint_type);
+        EnableWindow(control,opt.is_active);
 
         SendMessageA(control,CB_RESETCONTENT,0,0);
         /* initialize values */
-        if (opt->type == SANE_TYPE_STRING && opt->constraint_type !=
-                SANE_CONSTRAINT_NONE)
+        if (opt.type == TYPE_STRING && opt.constraint_type != CONSTRAINT_NONE)
         {
-            int j = 0;
             CHAR buffer[255];
-            while (opt->constraint.string_list[j]!=NULL)
-            {
-                SendMessageA(control,CB_ADDSTRING,0,
-                        (LPARAM)opt->constraint.string_list[j]);
-                j++;
-            }
+            WCHAR *p;
+
+            for (p = opt.constraint.strings; *p; p += strlenW(p) + 1)
+                SendMessageW( control,CB_ADDSTRING,0, (LPARAM)p );
             sane_option_get_value( i, buffer );
             SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer);
         }
-        else if (opt->type == SANE_TYPE_BOOL)
+        else if (opt.type == TYPE_BOOL)
         {
             BOOL b;
             sane_option_get_value( i, &b );
@@ -735,46 +712,43 @@ static INT_PTR InitializeDialog(HWND hwnd)
                 SendMessageA(control,BM_SETCHECK,BST_CHECKED,0);
 
         }
-        else if (opt->type == SANE_TYPE_INT &&
-                 opt->constraint_type == SANE_CONSTRAINT_WORD_LIST)
+        else if (opt.type == TYPE_INT && opt.constraint_type == CONSTRAINT_WORD_LIST)
         {
-            int j, count = opt->constraint.word_list[0];
+            int j, count = opt.constraint.word_list[0];
             CHAR buffer[16];
             int val;
             for (j=1; j<=count; j++)
             {
-                sprintf(buffer, "%d", opt->constraint.word_list[j]);
+                sprintf(buffer, "%d", opt.constraint.word_list[j]);
                 SendMessageA(control, CB_ADDSTRING, 0, (LPARAM)buffer);
             }
             sane_option_get_value( i, &val );
             sprintf(buffer, "%d", val);
             SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer);
         }
-        else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
+        else if (opt.constraint_type == CONSTRAINT_RANGE)
         {
-            if (opt->type == SANE_TYPE_INT)
+            if (opt.type == TYPE_INT)
             {
                 int si;
                 int min,max;
 
-                min = (SANE_Int)opt->constraint.range->min /
-                    (((SANE_Int)opt->constraint.range->quant)?
-                    (SANE_Int)opt->constraint.range->quant:1);
+                min = opt.constraint.range.min /
+                    (opt.constraint.range.quant ? opt.constraint.range.quant : 1);
 
-                max = (SANE_Int)opt->constraint.range->max /
-                    (((SANE_Int)opt->constraint.range->quant)
-                    ?(SANE_Int)opt->constraint.range->quant:1);
+                max = opt.constraint.range.max /
+                    (opt.constraint.range.quant ? opt.constraint.range.quant : 1);
 
                 SendMessageA(control,SBM_SETRANGE,min,max);
 
                 sane_option_get_value( i, &si );
-                if (opt->constraint.range->quant)
-                    si = si / opt->constraint.range->quant;
+                if (opt.constraint.range.quant)
+                    si = si / opt.constraint.range.quant;
 
                 SendMessageW(control,SBM_SETPOS, si, TRUE);
-                UpdateRelevantEdit(hwnd, opt, i, si);
+                UpdateRelevantEdit(hwnd, &opt, si);
             }
-            else if (opt->type == SANE_TYPE_FIXED)
+            else if (opt.type == TYPE_FIXED)
             {
                 SANE_Fixed *sf;
 
@@ -783,9 +757,9 @@ static INT_PTR InitializeDialog(HWND hwnd)
                 INT pos;
                 int min,max;
 
-                s_min = SANE_UNFIX(opt->constraint.range->min);
-                s_max = SANE_UNFIX(opt->constraint.range->max);
-                s_quant = SANE_UNFIX(opt->constraint.range->quant);
+                s_min = SANE_UNFIX(opt.constraint.range.min);
+                s_max = SANE_UNFIX(opt.constraint.range.max);
+                s_quant = SANE_UNFIX(opt.constraint.range.quant);
 
                 if (s_quant)
                 {
@@ -801,7 +775,7 @@ static INT_PTR InitializeDialog(HWND hwnd)
                 SendMessageA(control,SBM_SETRANGE,min,max);
 
 
-                sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
+                sf = HeapAlloc(GetProcessHeap(),0,opt.size*sizeof(SANE_Word));
                 sane_option_get_value( i, sf );
 
                 dd = SANE_UNFIX(*sf);
@@ -818,7 +792,7 @@ static INT_PTR InitializeDialog(HWND hwnd)
 
                 SendMessageW(control, SBM_SETPOS, pos, TRUE);
                 
-                UpdateRelevantEdit(hwnd, opt, i, pos);
+                UpdateRelevantEdit(hwnd, &opt, pos);
             }
         }
     }
@@ -828,19 +802,15 @@ static INT_PTR InitializeDialog(HWND hwnd)
 
 static INT_PTR ProcessScroll(HWND hwnd, WPARAM wParam, LPARAM lParam)
 {
-    int index;
-    const SANE_Option_Descriptor *opt;
+    struct option_descriptor opt;
     WORD scroll;
     DWORD position;
 
-    index = GetDlgCtrlID((HWND)lParam)- ID_BASE;
-    if (index < 0)
+    opt.optno = GetDlgCtrlID((HWND)lParam)- ID_BASE;
+    if (opt.optno < 0)
         return FALSE;
 
-    opt = sane_get_option_descriptor(activeDS.deviceHandle, index);
-
-    if (!opt)
-        return FALSE;
+    if (sane_option_get_descriptor( &opt )) return FALSE;
 
     scroll = LOWORD(wParam);
 
@@ -875,8 +845,8 @@ static INT_PTR ProcessScroll(HWND hwnd, WPARAM wParam, LPARAM lParam)
     SendMessageW((HWND)lParam,SBM_SETPOS, position, TRUE);
     position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
 
-    UpdateRelevantEdit(hwnd, opt, index, position);
-    if (UpdateSaneScrollOption(opt, index, position))
+    UpdateRelevantEdit(hwnd, &opt, position);
+    if (UpdateSaneScrollOption(&opt, position))
         InitializeDialog(hwnd);
 
     return TRUE;
@@ -885,44 +855,36 @@ static INT_PTR ProcessScroll(HWND hwnd, WPARAM wParam, LPARAM lParam)
 
 static void ButtonClicked(HWND hwnd, INT id, HWND control)
 {
-    int index;
-    const SANE_Option_Descriptor *opt;
+    struct option_descriptor opt;
     BOOL changed = FALSE;
 
-    index = id - ID_BASE;
-    if (index < 0)
+    opt.optno = id - ID_BASE;
+    if (opt.optno < 0)
         return;
 
-    opt = sane_get_option_descriptor(activeDS.deviceHandle, index);
+    if (sane_option_get_descriptor( &opt )) return;
 
-    if (!opt)
-        return;
-
-    if (opt->type == SANE_TYPE_BOOL)
+    if (opt.type == TYPE_BOOL)
     {
         BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED;
-        sane_option_set_value( index, &r, &changed );
+        sane_option_set_value( opt.optno, &r, &changed );
         if (changed) InitializeDialog(hwnd);
     }
 }
 
 static void ComboChanged(HWND hwnd, INT id, HWND control)
 {
-    int index;
     int selection;
     int len;
-    const SANE_Option_Descriptor *opt;
+    struct option_descriptor opt;
     SANE_String value;
     BOOL changed = FALSE;
 
-    index = id - ID_BASE;
-    if (index < 0)
+    opt.optno = id - ID_BASE;
+    if (opt.optno < 0)
         return;
 
-    opt = sane_get_option_descriptor(activeDS.deviceHandle, index);
-
-    if (!opt)
-        return;
+    if (sane_option_get_descriptor( &opt )) return;
 
     selection = SendMessageW(control,CB_GETCURSEL,0,0);
     len = SendMessageW(control,CB_GETLBTEXTLEN,selection,0);
@@ -931,14 +893,14 @@ static void ComboChanged(HWND hwnd, INT id, HWND control)
     value = HeapAlloc(GetProcessHeap(),0,len);
     SendMessageA(control,CB_GETLBTEXT,selection,(LPARAM)value);
 
-    if (opt->type == SANE_TYPE_STRING)
+    if (opt.type == TYPE_STRING)
     {
-        sane_option_set_value( index, value, &changed );
+        sane_option_set_value( opt.optno, value, &changed );
     }
-    else if (opt->type == SANE_TYPE_INT)
+    else if (opt.type == TYPE_INT)
     {
         int val = atoi( value );
-        sane_option_set_value( index, &val, &changed );
+        sane_option_set_value( opt.optno, &val, &changed );
     }
     if (changed) InitializeDialog(hwnd);
 }
-- 
GitLab