Exemplo n.º 1
0
static void
create_images(const X3PFile *x3pfile, GwyContainer *container)
{
    gint id;

    for (id = 0; id < x3pfile->zres; id++) {
        GwyContainer *meta;
        guint n = x3pfile->xres*x3pfile->yres, k;
        GwyDataField *dfield, *mask;
        const gboolean *valid = x3pfile->valid + id*n;
        GQuark quark;
        gchar buf[40];

        dfield = gwy_data_field_new(x3pfile->xres,
                                    x3pfile->yres,
                                    x3pfile->xres*x3pfile->dx,
                                    x3pfile->yres*x3pfile->dy,
                                    FALSE);
        memcpy(dfield->data, x3pfile->values + id*n, n*sizeof(gdouble));
        for (k = 0; k < n; k++) {
            if (!valid[k])
                dfield->data[k] = NAN;
        }

        quark = gwy_app_get_data_key_for_id(id);
        gwy_container_set_object(container, quark, dfield);

        gwy_si_unit_set_from_string(gwy_data_field_get_si_unit_xy(dfield), "m");
        gwy_si_unit_set_from_string(gwy_data_field_get_si_unit_z(dfield), "m");
        gwy_app_channel_title_fall_back(container, id);
        gwy_app_channel_check_nonsquare(container, id);

        if ((mask = gwy_app_channel_mask_of_nans(dfield, TRUE))) {
            quark = gwy_app_get_mask_key_for_id(id);
            gwy_container_set_object(container, quark, mask);
            g_object_unref(mask);
        }
        g_object_unref(dfield);

        if ((meta = get_meta(x3pfile))) {
            g_snprintf(buf, sizeof(buf), "/%u/meta", id);
            gwy_container_set_object_by_name(container, buf, meta);
            g_object_unref(meta);
        }
    }
}
Exemplo n.º 2
0
static GwyContainer*
shimadzu_load(const gchar *filename,
              G_GNUC_UNUSED GwyRunType mode,
              GError **error)
{
    GwyContainer *meta, *container = NULL;
    GwyDataField *dfield = NULL;
    GError *err = NULL;
    gchar *buffer = NULL;
    GHashTable *hash;
    gchar *head;
    gsize size = 0;
    gboolean ok;
    gint text_data_start;

    if (!g_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }
    if (size < HEADER_SIZE + 2) {
        err_TOO_SHORT(error);
        return NULL;
    }
    if (memcmp(buffer, MAGIC, MAGIC_SIZE) != 0
        && !(memcmp(buffer, MAGIC_ASCII, MAGIC_ASCII_SIZE) == 0
             && (memcmp(buffer + MAGIC_ASCII_SIZE+1,
                        MAGIC, MAGIC_SIZE) == 0
                 || memcmp(buffer + MAGIC_ASCII_SIZE+2,
                           MAGIC, MAGIC_SIZE) == 0))) {
        err_FILE_TYPE(error, "Shimadzu");
        g_free(buffer);
        return NULL;
    }

    head = g_memdup(buffer, HEADER_SIZE+1);
    head[HEADER_SIZE] = '\0';

    /* text_data_start is set to nonzero if data are text */
    hash = read_hash(head, &text_data_start, error);
    ok = !!hash;
    if (ok) {
        if (text_data_start)
            dfield = read_text_data(buffer, text_data_start, hash, error);
        else
            dfield = read_binary_data(buffer, size, hash, error);

        ok = !!dfield;
    }

    if (ok) {
        GQuark quark;
        const gchar *title;

        container = gwy_container_new();
        quark = gwy_app_get_data_key_for_id(0);
        gwy_container_set_object(container, quark, dfield);
        g_object_unref(dfield);

        meta = shimadzu_get_metadata(hash);
        gwy_container_set_object_by_name(container, "/0/meta", meta);
        g_object_unref(meta);

        title = g_hash_table_lookup(hash, "Channel");
        if (title && *title)
            gwy_container_set_string_by_name(container, "/0/data/title",
                                             g_strdup(title));
        else
            gwy_app_channel_title_fall_back(container, 0);

        gwy_file_channel_import_log_add(container, 0, NULL, filename);
    }

    g_free(head);
    g_free(buffer);
    g_hash_table_destroy(hash);

    return container;
}
Exemplo n.º 3
0
static GwyContainer*
sly_load(const gchar *filename,
         G_GNUC_UNUSED GwyRunType mode,
         GError **error)
{
    GwyContainer *container = NULL, *meta = NULL;
    gchar *buffer = NULL;
    GError *err = NULL;
    GHashTable *hash = NULL;
    gchar *p, *line, *value;
    guint expecting_data = 0;
    SensolyticsChannel *channels = NULL;
    Dimensions dimensions;
    gint ndata = 0, i;

    if (!g_file_get_contents(filename, &buffer, NULL, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    p = buffer;
    line = gwy_str_next_line(&p);
    g_strstrip(line);
    if (!gwy_strequal(line, MAGIC)) {
        err_FILE_TYPE(error, "Sensolytics");
        goto fail;
    }

    hash = g_hash_table_new(g_str_hash, g_str_equal);
    for (line = gwy_str_next_line(&p); line; line = gwy_str_next_line(&p)) {
        if (!line[0])
            continue;

        if (expecting_data) {
            expecting_data--;

            /* The columns are comma-separated and numbers use decimal points.
             * Do not tempt the number parsing functions more than necessary
             * and fix commas to tab characters. */
            g_strdelimit(line, ",", '\t');

            /* Ignore X, Y and Z, each is two values */
            for (i = 0; i < 6; i++)
                g_ascii_strtod(line, &line);

            for (i = 0; i < ndata; i++)
                channels[i].data[expecting_data]
                    = channels[i].q * g_ascii_strtod(line, &line);
        }
        else {
            g_strstrip(line);
            if (line[0] != '#') {
                g_warning("Comment line does not start with #.");
                continue;
            }

            do {
                line++;
            } while (g_ascii_isspace(*line));

            if (g_str_has_prefix(line, "X [")) {
                if (channels) {
                    g_warning("Multiple data headers!?");
                    continue;
                }

                if (!read_dimensions(hash, &ndata, &dimensions, error)
                    || !(channels = create_fields(hash, line,
                                                  ndata, &dimensions)))
                    goto fail;
                expecting_data = dimensions.xres * dimensions.yres;
                continue;
            }

            value = strchr(line, ':');
            if (!value) {
                if (!gwy_strequal(line, "ArrayScan"))
                    g_warning("Non-parameter-like line %s", line);
                continue;
            }
            *value = '\0';
            g_strchomp(line);
            do {
                value++;
            } while (g_ascii_isspace(*value));

            if (gwy_strequal(line, "Warning"))
                continue;

            gwy_debug("<%s>=<%s>", line, value);
            g_hash_table_insert(hash, line, value);
        }
    }

    if (!channels) {
        err_NO_DATA(error);
        goto fail;
    }

    container = gwy_container_new();
    for (i = 0; i < ndata; i++) {
        GQuark key = gwy_app_get_data_key_for_id(i);

        gwy_data_field_invert(channels[i].dfield, FALSE, TRUE, FALSE);
        gwy_container_set_object(container, key, channels[i].dfield);
        gwy_app_channel_check_nonsquare(container, i);
        if (channels[i].name) {
            gchar *s = g_strconcat(g_quark_to_string(key), "/title", NULL);
            gwy_container_set_string_by_name(container, s,
                                             g_strdup(channels[i].name));
            g_free(s);
        }
        else
            gwy_app_channel_title_fall_back(container, i);

        gwy_file_channel_import_log_add(container, i, NULL, filename);
    }

    meta = get_meta(hash);
    clone_meta(container, meta, ndata);
    g_object_unref(meta);

fail:
    g_free(buffer);
    if (hash)
        g_hash_table_destroy(hash);
    if (channels) {
        for (i = 0; i < ndata; i++)
            g_object_unref(channels[i].dfield);
        g_free(channels);
    }

    return container;
}
Exemplo n.º 4
0
static GwyContainer*
mif_load(const gchar *filename,
         G_GNUC_UNUSED GwyRunType mode,
         GError **error)
{
    GwyContainer *container = NULL;
    guchar *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    MIFFile mfile;
    GwyDataField *dfield;
    guint i;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    if (!mif_read_header(buffer, size, &mfile.header, error)) {
        gwy_file_abandon_contents(buffer, size, NULL);
        return NULL;
    }
    /* TODO: Check file version */
    if (!mif_read_image_items(mfile.images, buffer, size, &mfile.header.info,
                              error)) {
        gwy_file_abandon_contents(buffer, size, NULL);
        return NULL;
    }

    /* FIXME: Only v1.7! */
    for (i = 0; i < mfile.header.nimages; i++) {
        MIFInfoItem *item = mfile.images + i;
        MIFImageHeader image_header;
        MIFBlock raster, macro_geometry, preview, image, curve, calc;
        guint ncalculations;
        const guchar *p = buffer + item->image.offset;
        GQuark quark;

        if (!item->image.size)
            continue;
        if (item->image.offset > size
            || item->image.size > size
            || item->image.offset + item->image.size > size) {
            continue;
        }

        /* XXX: We cannot use item->image.size because it's bogus, i.e.
         * too short.  Apparently there is some unaccounted-for space until
         * the next block starts, 120 bytes for v1.7 files, after the image
         * header which is in fact still occupied by the image header.
         * MIFBlock says 714 bytes but the true size is 834 = 714 + 120. */
        if (!mif_read_image_header(&image_header, &p, size - (p - buffer),
                                   mfile.header.file_version,
                                   error))
            continue;

        if (p - buffer + 52 > size)
            continue;

        mif_read_block(&raster, "raster", &p);
        mif_read_block(&macro_geometry, "macro_geometry", &p);
        mif_read_block(&preview, "preview", &p);
        mif_read_block(&image, "image", &p);
        mif_read_block(&curve, "curve", &p);
        ncalculations = gwy_get_guint32_le(&p);
        mif_read_block(&calc, "calc", &p);

        gwy_debug("image header true size: %zu",
                  (gsize)(p - (buffer + item->image.offset)));

        dfield = mif_read_data_field(&image_header, &image, buffer, size, NULL);
        if (!dfield)
            continue;

        if (!container)
            container = gwy_container_new();

        quark = gwy_app_get_data_key_for_id(i);
        gwy_container_set_object(container, quark, dfield);
        g_object_unref(dfield);
        gwy_app_channel_title_fall_back(container, i);
    }

    if (!container)
        err_NO_DATA(error);

    gwy_file_abandon_contents(buffer, size, NULL);
    return container;
}
Exemplo n.º 5
0
static GwyContainer*
burleigh_exp_load(const gchar *filename,
                  G_GNUC_UNUSED GwyRunType mode,
                  GError **error)
{
    GwyContainer *container = NULL;
    gchar *buffer = NULL;
    BurleighExpHeader header;
    gsize size = 0;
    GError *err = NULL;
    GwyDataField *dfield;
    gdouble *data;
    guint i, n;

    if (!g_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }
    if (size < MIN_FILE_SIZE + 2) {
        err_TOO_SHORT(error);
        g_free(buffer);
        return NULL;
    }

    if (!burleigh_exp_read_header(&header, buffer, error))
        goto fail;

    n = header.xres * header.yres;
    if (header.binary) {
        if (header.bpp != 16) {
            err_BPP(error, header.bpp);
            goto fail;
        }
        else if (err_SIZE_MISMATCH(error, header.length + 2*n, size, TRUE))
            goto fail;
    }

    dfield = gwy_data_field_new(header.xres, header.yres,
                                header.xscale, header.yscale,
                                FALSE);
    data = gwy_data_field_get_data(dfield);

    if (header.binary) {
        const gint16 *d16 = (const gint16*)(buffer + header.length);

        for (i = 0; i < n; i++)
            data[i] = GINT16_FROM_LE(d16[i]);
    }
    else {
        gchar *p = buffer + header.length;

        for (i = 0; i < n; i++)
            data[i] = strtol(p, &p, 10);
    }

    gwy_data_field_multiply(dfield, header.zscale/32768.0);

    /* Units references released in free_header() */
    gwy_data_field_set_si_unit_xy(dfield, header.xyunits);
    gwy_data_field_set_si_unit_z(dfield, header.zunits);

    container = gwy_container_new();
    gwy_container_set_object(container, gwy_app_get_data_key_for_id(0), dfield);
    g_object_unref(dfield);
    gwy_app_channel_title_fall_back(container, 0);
    gwy_file_channel_import_log_add(container, 0, NULL, filename);

fail:
    free_header(&header);
    g_free(buffer);

    return container;
}
Exemplo n.º 6
0
static GwyContainer*
rhkspm32_load(const gchar *filename,
              G_GNUC_UNUSED GwyRunType mode,
              GError **error)
{
    GArray *rhkfile;
    RHKPage *rhkpage;
    GwyContainer *meta, *container = NULL;
    guchar *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    GwyDataField *dfield = NULL;
    gsize totalpos, pagesize;
    GString *key;
    guint i;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }
    if (size < HEADER_SIZE) {
        err_TOO_SHORT(error);
        gwy_file_abandon_contents(buffer, size, NULL);
        return NULL;
    }

    // niv - rhkfile is an array of rhkpage's, but buffer is where the actual
    // raw file data is stored
    rhkfile = g_array_new(FALSE, TRUE, sizeof(RHKPage));
    totalpos = 0;

    while (totalpos < size) {
        g_array_set_size(rhkfile, rhkfile->len + 1);
        rhkpage = &g_array_index(rhkfile, RHKPage, rhkfile->len - 1);
        rhkpage->buffer = buffer + totalpos;
        // niv - if the header seems illegal, skip all the next ones as well
        // (and cancel the element addition to the g_array)
        if (!rhkspm32_read_header(rhkpage, &err)) {
            g_warning("failed to read rhk header after %u", rhkfile->len);
            g_array_set_size(rhkfile, rhkfile->len - 1);
            break;
        }

        pagesize = rhkpage->data_offset
                   + rhkpage->item_size*rhkpage->xres*rhkpage->yres;
        if (size < totalpos + pagesize) {
            rhkspm32_free(rhkpage);
            g_array_set_size(rhkfile, rhkfile->len - 1);
            break;
        }

        totalpos += pagesize;
    }

    /* Be tolerant and don't fail when we were able to import at least
     * something */
    if (!rhkfile->len) {
        if (err)
            g_propagate_error(error, err);
        else
            err_NO_DATA(error);
        gwy_file_abandon_contents(buffer, size, NULL);
        g_array_free(rhkfile, TRUE);
        return NULL;
    }
    g_clear_error(&err);

    container = gwy_container_new();
    key = g_string_new(NULL);
    for (i = 0; i < rhkfile->len; i++) {
        const gchar *cs;
        gchar *s;

        gwy_debug("rhk-spm32: processing page %d of %d\n", i+1, rhkfile->len);
        rhkpage = &g_array_index(rhkfile, RHKPage, i);
        if (rhkpage->type == RHK_TYPE_IMAGE) { // niv - just leaving this alone
            dfield = rhkspm32_read_data(rhkpage);
            g_string_printf(key, "/%d/data", i);
            gwy_container_set_object_by_name(container, key->str, dfield);
            g_object_unref(dfield);
            g_string_append(key, "/title");
            cs = gwy_enum_to_string(rhkpage->scan,
                                    scan_directions,
                                    G_N_ELEMENTS(scan_directions));
            if (rhkpage->label) {
                if (cs)
                    s = g_strdup_printf("%s [%s]", rhkpage->label, cs);
                else
                    s = g_strdup(rhkpage->label);
                gwy_container_set_string_by_name(container, key->str, s);
            }
            else
                gwy_app_channel_title_fall_back(container, i);

            gwy_file_channel_import_log_add(container, i, NULL,
                                            filename);
        }
        else if (rhkpage->type == RHK_TYPE_LINE) { // niv - after omicron.c

            GwySpectra* spectra;
            GwyGraphModel *gmodel;

            spectra = rhkspm32_read_spectra(rhkpage);
            /* converting to graphs, as there is no point in leaving these as
             * sps - no xy coordinates, so the spectro tool is kinda clueless */
            gwy_debug("processing graph in page %d\n", i);
            if ((gmodel = spectra_to_graph(spectra)) != NULL) {
                gchar *container_key = NULL;
                /* add gmodel to container */
                container_key = g_strdup_printf("%s/%d", GRAPH_PREFIX, i);
                gwy_container_set_object_by_name(container, container_key,
                                                gmodel);
                g_free(container_key);
            }
            g_object_unref(gmodel);
            g_object_unref(spectra);
        }
        gwy_debug("rhk-spm32: finished parsing page %d \n", i);
        meta = rhkspm32_get_metadata(rhkpage);
        if (rhkpage->type == RHK_TYPE_IMAGE) {
            /* this doesn't really work, but at least the meta data stays
               with the graph, even if the metadata viewer won't show it*/
            g_string_printf(key, "/%d/meta", i);
        }
        else if (rhkpage->type == RHK_TYPE_LINE) {
            g_string_printf(key, "%s/%d/meta", GRAPH_PREFIX, i);
        }
        gwy_container_set_object_by_name(container, key->str, meta);
        g_object_unref(meta);
    }
    g_string_free(key, TRUE);

    gwy_file_abandon_contents(buffer, size, NULL);
    for (i = 0; i < rhkfile->len; i++)
        rhkspm32_free(&g_array_index(rhkfile, RHKPage, i));
    g_array_free(rhkfile, TRUE);

    return container;
}
Exemplo n.º 7
0
static GwyContainer*
plt_load(const gchar *filename,
         G_GNUC_UNUSED GwyRunType mode,
         GError **error)
{
    GwyContainer *container = NULL;
    GwyDataField *dfield = NULL;
    GwyTextHeaderParser parser;
    GwySIUnit *xunit, *yunit, *zunit;
    gchar *p, *value, *buffer = NULL;
    GHashTable *hash = NULL;
    gsize size;
    GError *err = NULL;
    G_GNUC_UNUSED gdouble xreal, yreal, zreal;
    gint i, xres, yres;
    gdouble *data;

    if (!g_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        goto fail;
    }

    if (strncmp(buffer, MAGIC1, MIN(size, sizeof(MAGIC1)-1))) {
        err_FILE_TYPE(error, "Nanosurf PLT");
        goto fail;
    }

    /* Find the first line not starting with '#' */
    for (p = buffer; (p - buffer) + 1 < size; p++) {
        if ((p[0] == '\n' || p[0] == '\r')
            && (p[1] != '\n' && p[1] != '#')) {
            break;
        }
    }
    *p = '\0';
    p++;

    gwy_clear(&parser, 1);
    parser.line_prefix = "#";
    parser.key_value_separator = ":";
    hash = gwy_text_header_parse(buffer, &parser, NULL, NULL);
    if (!require_keys(hash, error,
                      "Channel", "Lines", "Points",
                      "XRange", "YRange", "ZRange",
                      NULL))
        goto fail;

    xres = atoi(g_hash_table_lookup(hash, "Points"));
    yres = atoi(g_hash_table_lookup(hash, "Lines"));
    if (err_DIMENSION(error, xres) || err_DIMENSION(error, yres))
        goto fail;

    value = g_hash_table_lookup(hash, "XRange");
    xreal = g_ascii_strtod(value, &value);
    xunit = gwy_si_unit_new(value);

    value = g_hash_table_lookup(hash, "YRange");
    yreal = g_ascii_strtod(value, &value);
    yunit = gwy_si_unit_new(value);

    value = g_hash_table_lookup(hash, "ZRange");
    zreal = g_ascii_strtod(value, &value);
    zunit = gwy_si_unit_new(value);

    /* Use negated positive conditions to catch NaNs */
    if (!((xreal = fabs(xreal)) > 0)) {
        g_warning("Real x size is 0.0, fixing to 1.0");
        xreal = 1.0;
    }
    if (!((yreal = fabs(yreal)) > 0)) {
        g_warning("Real y size is 0.0, fixing to 1.0");
        yreal = 1.0;
    }

    if (!gwy_si_unit_equal(xunit, yunit))
        g_warning("X and Y units differ, using X");

    dfield = gwy_data_field_new(xres, yres, xreal, yreal, FALSE);
    gwy_data_field_set_si_unit_xy(dfield, xunit);
    gwy_data_field_set_si_unit_z(dfield, zunit);
    g_object_unref(xunit);
    g_object_unref(yunit);
    g_object_unref(zunit);

    data = gwy_data_field_get_data(dfield);
    value = p;
    for (i = 0; i < xres*yres; i++) {
        data[i] = g_ascii_strtod(value, &p);
        value = p;
    }

    container = gwy_container_new();
    gwy_container_set_object(container, gwy_app_get_data_key_for_id(0), dfield);
    g_object_unref(dfield);

    if ((value = g_hash_table_lookup(hash, "Channel")))
        gwy_container_set_string_by_name(container, "/0/data/title",
                                         g_strdup(value));
    else
        gwy_app_channel_title_fall_back(container, 0);

    gwy_file_channel_import_log_add(container, 0, NULL, filename);

fail:
    g_free(buffer);
    g_hash_table_destroy(hash);

    return container;
}
Exemplo n.º 8
0
static GwyContainer*
unisoku_load(const gchar *filename,
             G_GNUC_UNUSED GwyRunType mode,
             GError **error)
{
    UnisokuFile ufile;
    GwyContainer *meta, *container = NULL;
    guchar *buffer = NULL;
    gchar *text = NULL;
    gsize i, size = 0;
    GError *err = NULL;
    GwyDataField *dfield = NULL;
    gchar *data_name;

    if (!g_file_get_contents(filename, &text, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    for (i = 0; i < size; i++) {
        if (!text[i])
            text[i] = ' ';
    }

    gwy_clear(&ufile, 1);
    if (!unisoku_read_header(text, &ufile, error)) {
        unisoku_file_free(&ufile);
        g_free(text);
        return NULL;
    }
    g_free(text);

    if (ufile.data_type < UNISOKU_UINT8
        || ufile.data_type > UNISOKU_FLOAT
        || type_sizes[ufile.data_type] == 0) {
        err_DATA_TYPE(error, ufile.data_type);
        unisoku_file_free(&ufile);
        return NULL;
    }

    if (!(data_name = unisoku_find_data_name(filename))) {
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("No corresponding data file was found for header file."));
        unisoku_file_free(&ufile);
        return NULL;
    }

    if (!gwy_file_get_contents(data_name, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        unisoku_file_free(&ufile);
        g_free(data_name);
        return NULL;
    }

    dfield = unisoku_read_data_field(buffer, size, &ufile, error);
    gwy_file_abandon_contents(buffer, size, NULL);

    if (!dfield) {
        unisoku_file_free(&ufile);
        g_free(data_name);
        return NULL;
    }

    container = gwy_container_new();
    gwy_container_set_object_by_name(container, "/0/data", dfield);
    g_object_unref(dfield);
    gwy_app_channel_title_fall_back(container, 0);

    meta = unisoku_get_metadata(&ufile);
    gwy_container_set_object_by_name(container, "/0/meta", meta);
    g_object_unref(meta);

    gwy_file_channel_import_log_add(container, 0, NULL, data_name);

    unisoku_file_free(&ufile);
    g_free(data_name);

    return container;
}
Exemplo n.º 9
0
static GwyContainer*
spmlab_load(const gchar *filename,
            G_GNUC_UNUSED GwyRunType mode,
            GError **error)
{
    GwyContainer *container = NULL;
    guchar *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    GwyDataField *dfield = NULL;
    gchar *title = NULL;
    gint dir;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }
    /* 2048 is wrong. moreover it differs for r5 and r4, kasigra uses 5752 for
     * r5 */
    if (size < 2048) {
        err_TOO_SHORT(error);
        gwy_file_abandon_contents(buffer, size, NULL);
        return NULL;
    }
    if (buffer[0] != '#' || buffer[1] != 'R') {
        err_FILE_TYPE(error, "Thermicroscopes SpmLab");
        gwy_file_abandon_contents(buffer, size, NULL);
        return NULL;
    }
    switch (buffer[2]) {
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
        dfield = read_data_field(buffer, size, buffer[2], &title, &dir, error);
        break;

    default:
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("Unknown format version %c."), buffer[2]);
        break;
    }

    gwy_file_abandon_contents(buffer, size, NULL);
    if (!dfield)
        return NULL;

    container = gwy_container_new();
    gwy_container_set_object_by_name(container, "/0/data", dfield);
    g_object_unref(dfield);

    if (title)
        gwy_container_set_string_by_name(container, "/0/data/title", title);
    else
        gwy_app_channel_title_fall_back(container, 0);


    gwy_file_channel_import_log_add(container, 0, NULL, filename);
    /* TODO: Store direction to metadata, if known */

    return container;
}
Exemplo n.º 10
0
static GwyContainer*
csmfile_load(const gchar *filename,
             G_GNUC_UNUSED GwyRunType mode,
             GError **error)
{
    GwyContainer *meta, *container = NULL;
    GHashTable *hash = NULL;
    guchar *d24, *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    GwyDataField *dfield = NULL;
    guint xres, yres, bmpsize, header_size, maxval, i, j;
    gdouble real, zmin, zmax, q, z0;
    GwyTextHeaderParser parser;
    GwySIUnit *unit = NULL;
    gchar *value, *end, *header = NULL;
    gdouble *data;
    gint power10;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    if (size < BMP_HEADER_SIZE) {
        err_TOO_SHORT(error);
        goto fail;
    }
    if (!read_bmp_header(buffer, &xres, &yres, &bmpsize)
        || size <= bmpsize) {
        err_FILE_TYPE(error, "CSM");
        goto fail;
    }

    header_size = size - bmpsize;
    header = g_new(gchar, header_size + 1);
    memcpy(header, buffer + bmpsize, header_size);
    header[header_size] = '\0';

    gwy_clear(&parser, 1);
    parser.key_value_separator = "=";
    hash = gwy_text_header_parse(header, &parser, NULL, NULL);

    /* Do NOT use the fields Image width, Image height from the added footer.
     * Even though it is specifically added by Benyuan it can disagree with the
     * BMP diemsions and when they disagree the BMP diemsions are apparently
     * right. */
    if (err_DIMENSION(error, xres))
        goto fail;
    if (err_DIMENSION(error, yres))
        goto fail;

    if (!(value = g_hash_table_lookup(hash, "ScanSize"))) {
        err_MISSING_FIELD(error, "ScanSize");
        goto fail;
    }
    real = g_ascii_strtod(value, NULL);
    /* Use negated positive conditions to catch NaNs */
    if (!((real = fabs(real)) > 0)) {
        g_warning("Real size is 0.0, fixing to 1.0");
        real = 1.0;
    }

    if (!(value = g_hash_table_lookup(hash, "HeightScale"))) {
        err_MISSING_FIELD(error, "HeightScale");
        goto fail;
    }
    zmax = g_ascii_strtod(value, &end);
    unit = gwy_si_unit_new_parse(end, &power10);

    /* Optional stuff for which we try to fall back. */
    if (!(value = g_hash_table_lookup(hash, "StartHeightScale")))
        zmin = 0.0;
    else
        zmin = g_ascii_strtod(value, NULL);

    if (!(value = g_hash_table_lookup(hash, "MaxValue")))
        maxval = 0xffff;
    else
        maxval = MAX(atoi(value), 1);


    dfield = gwy_data_field_new(xres, yres, real*Nanometre, real*Nanometre,
                                FALSE);
    data = gwy_data_field_get_data(dfield);

    d24 = buffer + BMP_HEADER_SIZE;
    q = pow10(power10)*(zmax - zmin)/maxval;
    z0 = pow10(power10)*zmin;
    for (i = 0; i < yres; i++) {
        gdouble *row = data + (yres-1 - i)*xres;
        for (j = 0; j < xres; j++, row++, d24 += 3)
            *row = (d24[0] + 256.0*d24[1])*q + z0;
    }

    gwy_si_unit_set_from_string(gwy_data_field_get_si_unit_xy(dfield), "m");
    gwy_data_field_set_si_unit_z(dfield, unit);

    container = gwy_container_new();
    gwy_container_set_object_by_name(container, "/0/data", dfield);
    g_object_unref(dfield);

    meta = gwy_container_new();
    g_hash_table_foreach(hash, store_meta, meta);
    gwy_container_set_object_by_name(container, "/0/meta", meta);
    g_object_unref(meta);

    if ((value = g_hash_table_lookup(hash, "sTitle"))
        && g_utf8_validate(value, -1, NULL)) {
        gwy_container_set_string_by_name(container, "/0/data/title",
                                         g_strdup(value));
    }
    else
        gwy_app_channel_title_fall_back(container, 0);

    gwy_file_channel_import_log_add(container, 0, NULL, filename);

fail:
    gwy_file_abandon_contents(buffer, size, NULL);
    GWY_OBJECT_UNREF(unit);
    if (header)
        g_free(header);
    if (hash)
        g_hash_table_destroy(hash);

    return container;
}
Exemplo n.º 11
0
static GwyContainer*
sensofar_load(const gchar *filename,
              G_GNUC_UNUSED GwyRunType mode,
              GError **error)
{
    SensofarDataDesc data_desc;
    GwyContainer *container = NULL;
    GwyDataField *dfield, *mfield;
    GwyGraphModel *gmodel;
    guchar *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    const guchar *p;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }
    if (size < HEADER_SIZE + 12) {
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("File header is truncated"));
        gwy_file_abandon_contents(buffer, size, NULL);
        return NULL;
    }

    /* Date block */
    p = buffer;
    memcpy(&data_desc.date.str, p, DATE_SIZE);
    data_desc.date.str[DATE_SIZE-1] = '\0';
    p += DATE_SIZE;
    data_desc.date.t = gwy_get_guint32_le(&p);

    /* Comment block */
    memcpy(&data_desc.user_comment, p, COMMENT_SIZE);
    data_desc.user_comment[COMMENT_SIZE-1] = '\0';
    p += COMMENT_SIZE;

    /* Calbration block */
    data_desc.axes_config.yres = gwy_get_guint32_le(&p);
    data_desc.axes_config.xres = gwy_get_guint32_le(&p);
    data_desc.axes_config.N_tall = gwy_get_guint32_le(&p);
    data_desc.axes_config.dy_multip = gwy_get_gfloat_le(&p);
    data_desc.axes_config.mppx = gwy_get_gfloat_le(&p);
    data_desc.axes_config.mppy = gwy_get_gfloat_le(&p);
    data_desc.axes_config.x_0 = gwy_get_gfloat_le(&p);
    data_desc.axes_config.y_0 = gwy_get_gfloat_le(&p);
    data_desc.axes_config.mpp_tall = gwy_get_gfloat_le(&p);
    data_desc.axes_config.z_0 = gwy_get_gfloat_le(&p);

    /* Measurement block */
    data_desc.measure_config.type = gwy_get_guint32_le(&p);
    data_desc.measure_config.algorithm = gwy_get_guint32_le(&p);
    data_desc.measure_config.method = gwy_get_guint32_le(&p);
    data_desc.measure_config.objective = gwy_get_guint32_le(&p);
    data_desc.measure_config.area = gwy_get_guint32_le(&p);
    data_desc.measure_config.xres_area = gwy_get_guint32_le(&p);
    data_desc.measure_config.yres_area = gwy_get_guint32_le(&p);
    data_desc.measure_config.xres = gwy_get_guint32_le(&p);
    data_desc.measure_config.yres = gwy_get_guint32_le(&p);
    data_desc.measure_config.na = gwy_get_guint32_le(&p);
    data_desc.measure_config.incr_z = gwy_get_gdouble_le(&p);
    data_desc.measure_config.range = gwy_get_gfloat_le(&p);
    data_desc.measure_config.n_planes = gwy_get_guint32_le(&p);
    data_desc.measure_config.tpc_umbral_F = gwy_get_guint32_le(&p);
    data_desc.measure_config.restore = gwy_get_gboolean8(&p);
    data_desc.measure_config.num_layers = *(p++);
    data_desc.measure_config.version = *(p++);
    data_desc.measure_config.config_hardware = *(p++);
    data_desc.measure_config.stack_im_num = *(p++);
    data_desc.measure_config.reserved = *(p++);
    p += 2; // struct padding
    data_desc.measure_config.factor_delmacio = gwy_get_guint32_le(&p);

    gwy_debug("File date=<%s>, data type=%d, xres=%d, yres=%d, version=%d", 
              data_desc.date.str, 
              data_desc.measure_config.type, 
              data_desc.measure_config.xres, 
              data_desc.measure_config.yres,
              data_desc.measure_config.version);

    switch (data_desc.measure_config.type) {
        case MES_TOPO:
        case MES_IMATGE:
        dfield = sensofar_read_data_field(&data_desc, &mfield,
                                          &p, size - (p - buffer), error);
        if (!dfield) {
            gwy_file_abandon_contents(buffer, size, NULL);
            return NULL;
        }

        container = gwy_container_new();
        gwy_container_set_object(container, gwy_app_get_data_key_for_id(0),
                                 dfield);
        g_object_unref(dfield);
        if (mfield) {
            gwy_container_set_object(container, gwy_app_get_mask_key_for_id(0),
                                     mfield);
            g_object_unref(mfield);
        }
        gwy_app_channel_title_fall_back(container, 0);
        break;

        case MES_PERFIL:
        case MES_GRUIX:
        gmodel = sensofar_read_profile(&data_desc,
                                       &p, size - (p - buffer), error);
        if (!gmodel) {
            gwy_file_abandon_contents(buffer, size, NULL);
            return NULL;
        }

        container = gwy_container_new();
        gwy_container_set_object(container, gwy_app_get_graph_key_for_id(0),
                                 gmodel);
        g_object_unref(gmodel);
        break;

        default:
        err_DATA_TYPE(error, data_desc.measure_config.type);
        break;
    }

    return container;
}
Exemplo n.º 12
0
static GwyContainer*
stmprg_load(const gchar *filename,
            G_GNUC_UNUSED GwyRunType mode,
            GError **error)
{
    GwyContainer *meta, *container = NULL;
    StmprgFile stmprgfile;
    guchar *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    GwyDataField *dfield;
    char *filename_ta, *ptr;
    gboolean ok;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }
    if (size < MAGIC_SIZE || memcmp(buffer, MAGIC_TXT, MAGIC_SIZE) != 0) {
        err_FILE_TYPE(error, "Omicron STMPRG");
        gwy_file_abandon_contents(buffer, size, NULL);
        return NULL;
    }

    gwy_clear(&stmprgfile, 1);
    ok = read_parameters(buffer, size, &stmprgfile, error);
    gwy_file_abandon_contents(buffer, size, NULL);
    if (!ok)
        return NULL;

    filename_ta = g_strdup(filename);
    ptr = filename_ta + strlen(filename_ta) - 1;
    while (g_ascii_isdigit(*ptr) && ptr > filename_ta+1)
        ptr--;

    if (ptr == filename_ta+1) {
        err_DATA_PART(error, filename);
        g_free(filename_ta);
        return NULL;
    }

    /* Accept lowercase and uppercase and try the same case for data file */
    if (*ptr == 'p' && *(ptr - 1) == 't')
        *ptr = 'a';
    if (*ptr == 'P' && *(ptr - 1) == 'T')
        *ptr = 'A';

    if (!gwy_file_get_contents(filename_ta, &buffer, &size, &err)) {
        g_free(filename_ta);
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }
    g_free(filename_ta);

    dfield = read_datafield(buffer, size, &stmprgfile, error);
    if (dfield) {
        container = gwy_container_new();
        gwy_container_set_object_by_name(container, "/0/data", dfield);
        g_object_unref(dfield);
        /* FIXME: with documentation, we could perhaps do better */
        gwy_app_channel_title_fall_back(container, 0);

        meta = stmprg_get_metadata(&stmprgfile);
        gwy_container_set_object_by_name(container, "/0/meta", meta);
        g_object_unref(meta);

        gwy_file_channel_import_log_add(container, 0, NULL, filename);
    }
    gwy_file_abandon_contents(buffer, size, NULL);

    return container;
}
Exemplo n.º 13
0
static GwyContainer*
quesant_load(const gchar *filename,
             G_GNUC_UNUSED GwyRunType mode,
             GError **error)
{
    GwyContainer *container = NULL;
    guchar *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    GwySIUnit *units = NULL;
    gint32 power10;
    FileInfo info;
    int row, col;
    GwyDataField *dfield;
    gdouble multiplier;
    const guint16 *p;
    gdouble *d;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    if (size <= HEADER_SIZE) {
        gwy_file_abandon_contents(buffer, size, NULL);
        err_TOO_SHORT(error);
        return NULL;
    }

    if (!read_file_info(buffer, size, &info, error)) {
        gwy_file_abandon_contents(buffer, size, NULL);
        return NULL;
    }

    // create units and datafield
    units = gwy_si_unit_new_parse("um", &power10);
    dfield = gwy_data_field_new(info.img_res, info.img_res, 
                                info.real_size * pow10(power10),
                                info.real_size * pow10(power10),
                                FALSE);
    // set units for XY axes
    gwy_data_field_set_si_unit_xy(dfield, units);
    g_object_unref(units);
    // set units for Z axis
    units = gwy_si_unit_new_parse("um", &power10);
    gwy_data_field_set_si_unit_z(dfield, units);
    g_object_unref(units);

    multiplier = info.z_scale * pow10(power10);
    p = info.image_data;
    d = gwy_data_field_get_data(dfield);
    // values are stored in unsigned int16 type
    for (row = 0; row < info.img_res; row++) {
        for (col = 0; col < info.img_res; col++) {
            d[row*info.img_res + col] = GUINT16_FROM_LE(*p) * multiplier;
            p++;
        }
    }

    // create container
    container = gwy_container_new();
    // put datafield into container
    gwy_container_set_object_by_name(container, "/0/data", dfield);
    g_object_unref(dfield);
    gwy_app_channel_title_fall_back(container, 0);
    gwy_file_channel_import_log_add(container, 0, NULL, filename);

    gwy_file_abandon_contents(buffer, size, NULL);
    return container;
}
Exemplo n.º 14
0
static GwyContainer*
gsf_load(const gchar *filename,
         G_GNUC_UNUSED GwyRunType mode,
         GError **error)
{
    GwyContainer *container = NULL, *meta = NULL;
    GwyDataField *dfield = NULL;
    GwyTextHeaderParser parser;
    GwySIUnit *unit;
    guchar *p, *value, *buffer = NULL, *header = NULL;
    const guchar *datap;
    GHashTable *hash = NULL;
    gsize size, expected_size;
    GError *err = NULL;
    gdouble xreal, yreal, xoff, yoff;
    guint i, xres, yres;
    gdouble *d;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    if (size < MAGIC_SIZE || memcmp(buffer, MAGIC, MAGIC_SIZE) != 0) {
        err_FILE_TYPE(error, "Gwyddion Simple Field");
        goto fail;
    }

    p = buffer + MAGIC_SIZE;
    datap = memchr(p, '\0', size - (p - buffer));
    if (!datap) {
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("File header is truncated."));
        goto fail;
    }
    header = g_strdup(p);
    datap += 4 - ((datap - buffer) % 4);

    gwy_clear(&parser, 1);
    parser.key_value_separator = "=";
    if (!(hash = gwy_text_header_parse(header, &parser, NULL, NULL))) {
        g_propagate_error(error, err);
        goto fail;
    }

    xres = read_pixel_size(hash, "XRes", error);
    yres = read_pixel_size(hash, "YRes", error);
    if (!xres || !yres)
        goto fail;

    expected_size = (datap - buffer) + sizeof(gfloat)*xres*yres;
    if (err_SIZE_MISMATCH(error, expected_size, size, TRUE))
        goto fail;

    xreal = read_real_size(hash, "XReal");
    yreal = read_real_size(hash, "YReal");
    dfield = gwy_data_field_new(xres, yres, xreal, yreal, FALSE);

    xoff = read_real_offset(hash, "XOffset");
    yoff = read_real_offset(hash, "YOffset");
    gwy_data_field_set_xoffset(dfield, xoff);
    gwy_data_field_set_yoffset(dfield, yoff);

    value = g_hash_table_lookup(hash, "XYUnits");
    unit = gwy_si_unit_new(value);
    gwy_data_field_set_si_unit_xy(dfield, unit);
    g_object_unref(unit);

    value = g_hash_table_lookup(hash, "ZUnits");
    unit = gwy_si_unit_new(value);
    gwy_data_field_set_si_unit_z(dfield, unit);
    g_object_unref(unit);

    d = gwy_data_field_get_data(dfield);
    for (i = xres*yres; i; i--)
        *(d++) = gwy_get_gfloat_le(&datap);

    container = gwy_container_new();
    gwy_container_set_object(container, gwy_app_get_data_key_for_id(0), dfield);
    g_object_unref(dfield);

    if ((value = g_hash_table_lookup(hash, "Title"))) {
        /* FIXME: Ensure valid UTF-8 */
        gwy_container_set_string_by_name(container, "/0/data/title",
                                         g_strdup(value));
    }
    else
        gwy_app_channel_title_fall_back(container, 0);

    meta = gwy_container_new();
    g_hash_table_foreach(hash, add_meta, meta);
    if (gwy_container_get_n_items(meta))
        gwy_container_set_object_by_name(container, "/0/meta", meta);
    g_object_unref(meta);

    gwy_file_channel_import_log_add(container, 0, NULL, filename);

fail:
    gwy_file_abandon_contents(buffer, size, NULL);
    if (header)
        g_free(header);
    if (hash)
        g_hash_table_destroy(hash);

    return container;
}
Exemplo n.º 15
0
static GwyContainer*
microprof_load(const gchar *filename,
               G_GNUC_UNUSED GwyRunType mode,
               GError **error)
{
    enum {
        XRES = 0x0026, YRES = 0x002a,
        XRANGE = 0x0038, YRANGE = 0x0040,
        ZRANGE = 0x006e,
    };
    GwyContainer *container = NULL;
    guchar *buffer = NULL;
    const guchar *p;
    MicroProfFile mfile;
    gsize size = 0;
    GError *err = NULL;
    guint i, ndata, datasize;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    if (size < MICROPROF_HEADER_SIZE) {
        err_TOO_SHORT(error);
        goto fail;
    }

    if (memcmp(buffer, MAGIC, MAGIC_SIZE) != 0) {
        err_FILE_TYPE(error, "MicroProf");
        goto fail;
    }

    p = buffer + XRES;
    mfile.xres = gwy_get_guint16_le(&p);
    if (err_DIMENSION(error, mfile.xres))
        goto fail;

    p = buffer + YRES;
    mfile.yres = gwy_get_guint16_le(&p);
    if (err_DIMENSION(error, mfile.xres))
        goto fail;

    p = buffer + XRANGE;
    mfile.xrange = gwy_get_gdouble_le(&p);
    if (!(mfile.xrange = fabs(mfile.xrange))) {
        g_warning("Real x size is 0.0, fixing to 1.0");
        mfile.xrange = 1.0;
    }

    p = buffer + YRANGE;
    mfile.yrange = gwy_get_gdouble_le(&p);
    if (!(mfile.yrange = fabs(mfile.yrange))) {
        g_warning("Real y size is 0.0, fixing to 1.0");
        mfile.yrange = 1.0;
    }

    p = buffer + ZRANGE;
    mfile.zscale = gwy_get_gdouble_le(&p);

    datasize = 2*mfile.xres*mfile.yres;
    if (err_SIZE_MISMATCH(error, datasize, size - MICROPROF_HEADER_SIZE, FALSE))
        goto fail;
    /* FIXME: There is weird stuff between channels.  Need specs.
    ndata = (size - MICROPROF_HEADER_SIZE)/datasize;
    if (!ndata) {
        err_NO_DATA(error);
        goto fail;
    }
    */
    ndata = 1;
    container = gwy_container_new();

    mfile.data = buffer + MICROPROF_HEADER_SIZE;
    for (i = 0; i < ndata; i++) {
        GwyDataField *dfield;
        GQuark quark;

        dfield = microprof_read_data_field(&mfile,
                                           mfile.data + i*datasize);
        quark = gwy_app_get_data_key_for_id(i);
        gwy_container_set_object(container, quark, dfield);
        g_object_unref(dfield);

        gwy_app_channel_title_fall_back(container, i);
        gwy_file_channel_import_log_add(container, i, NULL, filename);
    }

fail:
    gwy_file_abandon_contents(buffer, size, NULL);
    return container;
}
Exemplo n.º 16
0
static GwyContainer*
igor_load(const gchar *filename,
          G_GNUC_UNUSED GwyRunType mode,
          GError **error)
{
    GwyContainer *meta = NULL, *container = NULL;
    GwyDataField *dfield = NULL, *maskfield = NULL;
    GwyTextHeaderParser parser;
    IgorFile igorfile;
    IgorWaveHeader5 *wave5;
    GError *err = NULL;
    guchar *p, *buffer = NULL;
    gint xres, yres;
    gsize expected_size, size = 0;
    gchar *note = NULL;
    const gchar *value;
    gchar key[64];
    guint i, chid;
    GQuark quark;
    guint nlabels;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    gwy_clear(&igorfile, 1);
    if (!igor_read_headers(&igorfile, buffer, size, FALSE, error))
        goto fail;

    /* Only accept v5 files because older do not support 2D data */
    if (igorfile.header.version != 5) {
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("Format version is %d.  Only version 5 is supported."),
                    igorfile.header.version);
        goto fail;
    }

    /* Detect Asylum research files, leave it at generic if not detected. */
    if (memcmp(buffer + size-5, "MFP3D", 5) == 0)
        igorfile.variant = IGOR_ASYLUM_MPF3D;
    else if (memcmp(buffer + size-5, "Force", 5) == 0)
        igorfile.variant = IGOR_ASYLUM_FORCE;
    gwy_debug("producer variant %u", igorfile.variant);

    /* Must have exactly 3 dims: xres, yres, nchannels */
    wave5 = &igorfile.wave5;
    xres = wave5->n_dim[0];
    yres = wave5->n_dim[1];
    igorfile.nchannels = wave5->n_dim[2];
    if (igorfile.nchannels==0) igorfile.nchannels=1;

    if (!xres || !yres || !igorfile.nchannels || wave5->n_dim[3]) {
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("Only two-dimensional data are supported."));
        goto fail;
    }

    igorfile.type_size = igor_data_type_size(wave5->type);
    if (!igorfile.type_size) {
        err_DATA_TYPE(error, wave5->type);
        goto fail;
    }

    if (wave5->npts != xres*yres*igorfile.nchannels) {
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("Number of data points %u does not match resolutions "
                      "%u×%u×%u."),
                    wave5->npts, xres, yres, igorfile.nchannels);
        goto fail;
    }

    if (igorfile.header.wfm_size <= igorfile.wave_header_size) {
        err_INVALID(error, "wfmSize");
        goto fail;
    }

    expected_size = igorfile.header.wfm_size - igorfile.wave_header_size;
    if (expected_size != wave5->npts*igorfile.type_size) {
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("Data size %u does not match "
                      "the number of data points %u×%u."),
                    (guint)expected_size, wave5->npts, igorfile.type_size);
    }

    if (err_SIZE_MISMATCH(error, expected_size + igorfile.headers_size, size,
                          FALSE))
        goto fail;

    p = buffer + igorfile.headers_size + expected_size;
    gwy_debug("remaning data size: %lu", (gulong)(size - (p - buffer)));

    p += igorfile.header.formula_size;
    if ((igorfile.variant == IGOR_ASYLUM_FORCE
         || igorfile.variant == IGOR_ASYLUM_MPF3D)
        && igorfile.header.note_size
        && (p - buffer) + igorfile.header.note_size <= size) {
        note = g_strndup((const gchar*)p, size);
        gwy_clear(&parser, 1);
        parser.key_value_separator = ":";
        igorfile.meta = gwy_text_header_parse(note, &parser, NULL, NULL);
    }
    p += igorfile.header.note_size;

    /* FIXME: Support extended units for non-Asylum files! */
    p += igorfile.header.data_e_units_size;
    for (i = 0; i < MAXDIMS; i++)
        p += igorfile.header.dim_e_units_size[i];

    /* Skip labels of x and y dimension, we don't know what to do with them. */
    for (i = 0; i < 2; i++)
        p += igorfile.header.dim_labels_size[i];

    /* FIXME: The labels are mandatory only in Asylum Research files. */
    nlabels = igorfile.header.dim_labels_size[2]/(MAX_WAVE_NAME5+1);
    expected_size = (MAX_WAVE_NAME5 + 1)*(nlabels);
    if ((p - buffer) + expected_size > size ) {
        g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA,
                    _("Cannot read channel labels."));
        goto fail;
    }
    igorfile.titles = read_channel_labels(p, igorfile.nchannels+1, nlabels);
    p += igorfile.header.dim_labels_size[2];

    if (igorfile.meta) {
        igorfile.channel_info = g_new0(AsylumChannelInfo, igorfile.nchannels);
        for (i = 0; i < igorfile.nchannels; i++) {
            AsylumChannelInfo *chinfo = igorfile.channel_info + i;
            const gchar *title = g_ptr_array_index(igorfile.titles, i+1);

            if (title) {
                chinfo->name = canonicalize_title(title);
                g_snprintf(key, sizeof(key), "%sUnit", chinfo->name);
                value = g_hash_table_lookup(igorfile.meta, key);
                if (value)
                    chinfo->units = value;
                else
                    chinfo->units = channel_title_to_units(chinfo->name);
            }
        }
    }

    container = gwy_container_new();

    for (i = chid = 0; i < igorfile.nchannels; i++, chid++) {
        const gchar *title = g_ptr_array_index(igorfile.titles, i+1);
        const gchar *zunits = NULL;

        if (igorfile.channel_info) {
            AsylumChannelInfo *chinfo = igorfile.channel_info + i;
            zunits = chinfo->units;
            meta = igor_get_metadata(&igorfile, i + 1);
        }
        dfield = igor_read_data_field(&igorfile, buffer, i, zunits, FALSE);
        maskfield = gwy_app_channel_mask_of_nans(dfield, TRUE);
        quark = gwy_app_get_data_key_for_id(chid);
        gwy_container_set_object(container, quark, dfield);
        g_object_unref(dfield);
        if (maskfield) {
            g_snprintf(key, sizeof(key), "/%d/mask", chid);
            gwy_container_set_object_by_name(container, key, maskfield);
        }
        if (meta) {
            g_snprintf(key, sizeof(key), "/%d/meta", chid);
            gwy_container_set_object_by_name(container, key, meta);
        }

        if (title) {
            g_snprintf(key, sizeof(key), "/%d/data/title", chid);
            gwy_container_set_string_by_name(container, key, g_strdup(title));
        }
        gwy_app_channel_title_fall_back(container,chid);

        if (wave5->type & IGOR_COMPLEX) {
            chid++;
            dfield = igor_read_data_field(&igorfile, buffer, i, zunits, TRUE);
            quark = gwy_app_get_data_key_for_id(chid);
            gwy_container_set_object(container, quark, dfield);
            g_object_unref(dfield);
            if (meta) {
                g_snprintf(key, sizeof(key), "/%d/meta", chid);
                /* container still holds a reference */
                g_object_unref(meta);
                meta = gwy_container_duplicate(meta);
                gwy_container_set_object_by_name(container, key, meta);
            }
            if (maskfield) {
                g_snprintf(key, sizeof(key), "/%d/mask", chid);
                /* container still holds a reference */
                g_object_unref(maskfield);
                maskfield = gwy_data_field_duplicate(maskfield);
                gwy_container_set_object_by_name(container, key, maskfield);
            }

            if (title) {
                g_snprintf(key, sizeof(key), "/%d/data/title", chid);
                gwy_container_set_string_by_name(container, key, g_strdup(title));
            };
            gwy_app_channel_title_fall_back(container,chid);
        }
        gwy_object_unref(meta);
        gwy_object_unref(maskfield);

        gwy_file_channel_import_log_add(container, chid, NULL, filename);
    }

fail:
    gwy_file_abandon_contents(buffer, size, NULL);
    g_free(note);
    if (igorfile.channel_info) {
        for (i = 0; i < igorfile.nchannels; i++)
            g_free(igorfile.channel_info[i].name);
        g_free(igorfile.channel_info);
    }
    if (igorfile.meta)
        g_hash_table_destroy(igorfile.meta);
    if (igorfile.titles) {
        g_ptr_array_foreach(igorfile.titles, (GFunc)g_free, NULL);
        g_ptr_array_free(igorfile.titles, TRUE);
    }

    return container;
}