static GwyContainer* nanoscope_load(const gchar *filename, G_GNUC_UNUSED GwyRunType mode, GError **error) { GwyContainer *meta, *container = NULL; GError *err = NULL; gchar *buffer = NULL; gchar *p; const gchar *self; gsize size = 0; NanoscopeFileType file_type; NanoscopeData *ndata; NanoscopeValue *val; GHashTable *hash, *scannerlist = NULL, *scanlist = NULL; GList *l, *list = NULL; gint i, xres = 0, yres = 0; gboolean ok, has_version = FALSE; if (!g_file_get_contents(filename, &buffer, &size, &err)) { err_GET_FILE_CONTENTS(error, &err); return NULL; } file_type = NANOSCOPE_FILE_TYPE_NONE; if (size > MAGIC_SIZE) { if (!memcmp(buffer, MAGIC_TXT, MAGIC_SIZE)) file_type = NANOSCOPE_FILE_TYPE_TXT; else if (!memcmp(buffer, MAGIC_BIN, MAGIC_SIZE)) file_type = NANOSCOPE_FILE_TYPE_BIN; } if (!file_type) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("File is not a Nanoscope file, " "or it is a unknown subtype.")); g_free(buffer); return NULL; } gwy_debug("File type: %d", file_type); /* as already know file_type, fix the first char for hash reading */ *buffer = '\\'; p = buffer; while ((hash = read_hash(&p, &err))) { ndata = g_new0(NanoscopeData, 1); ndata->hash = hash; list = g_list_append(list, ndata); } if (err) { g_propagate_error(error, err); ok = FALSE; } else ok = TRUE; for (l = list; ok && l; l = g_list_next(l)) { ndata = (NanoscopeData*)l->data; hash = ndata->hash; self = g_hash_table_lookup(hash, "#self"); /* The alternate names were found in files written by some beast * called Nanoscope E software */ if (gwy_strequal(self, "Scanner list") || gwy_strequal(self, "Microscope list")) { scannerlist = hash; continue; } if (gwy_strequal(self, "File list")) { has_version = !!g_hash_table_lookup(hash, "Version"); gwy_debug("has Version: %d", has_version); continue; } if (gwy_strequal(self, "Ciao scan list") || gwy_strequal(self, "Afm list") || gwy_strequal(self, "NC Afm list")) { get_scan_list_res(hash, &xres, &yres); scanlist = hash; } if (!gwy_strequal(self, "Ciao image list") && !gwy_strequal(self, "AFM image list") && !gwy_strequal(self, "NCAFM image list")) continue; ndata->data_field = hash_to_data_field(hash, scannerlist, scanlist, file_type, has_version, size, buffer, xres, yres, &p, error); ok = ok && ndata->data_field; } if (ok) { gchar key[40]; i = 0; container = gwy_container_new(); for (l = list; l; l = g_list_next(l)) { ndata = (NanoscopeData*)l->data; if (ndata->data_field) { g_snprintf(key, sizeof(key), "/%d/data", i); gwy_container_set_object_by_name(container, key, ndata->data_field); if ((val = g_hash_table_lookup(ndata->hash, "@2:Image Data")) && val->soft_scale) { g_snprintf(key, sizeof(key), "/%d/data/title", i); gwy_container_set_string_by_name(container, key, g_strdup(val->soft_scale)); } meta = nanoscope_get_metadata(ndata->hash, list); g_snprintf(key, sizeof(key), "/%d/meta", i); gwy_container_set_object_by_name(container, key, meta); g_object_unref(meta); gwy_app_channel_check_nonsquare(container, i); i++; } } if (!i) gwy_object_unref(container); } for (l = list; l; l = g_list_next(l)) { ndata = (NanoscopeData*)l->data; gwy_object_unref(ndata->data_field); if (ndata->hash) g_hash_table_destroy(ndata->hash); g_free(ndata); } g_free(buffer); g_list_free(list); if (!container && ok) err_NO_DATA(error); return container; }
static GwySIUnit* get_physical_scale(GHashTable *hash, GHashTable *scannerlist, GHashTable *scanlist, gboolean has_version, gdouble *scale, GError **error) { GwySIUnit *siunit, *siunit2; NanoscopeValue *val, *sval; gchar *key; gint q; /* Very old style scales (files with Version field) */ if (!has_version) { if (!(val = g_hash_table_lookup(hash, "Z magnify image"))) { err_MISSING_FIELD(error, "Z magnify image"); return NULL; } /* TODO: Luminescence */ siunit = gwy_si_unit_new("m"); /* According to Markus, the units are 1/100 nm, but his scale * calculation is raw/655.36 [nm]. We have the factor 1/65536 applied * automatically, that gives 1e-7 [m]. Whatever. */ *scale = 1e-7 * val->hard_value; return siunit; } /* XXX: This is a damned heuristics. For some value types we try to guess * a different quantity scale to look up. */ if (!(val = g_hash_table_lookup(hash, "@2:Z scale"))) { if (!(val = g_hash_table_lookup(hash, "Z scale"))) { err_MISSING_FIELD(error, "Z scale"); return NULL; } /* Old style scales */ siunit = gwy_si_unit_new_parse(val->hard_value_units, &q); *scale = val->hard_value * pow10(q); if (val->hard_scale) *scale *= 65536.0/val->hard_scale; return siunit; } key = g_strdup_printf("@%s", val->soft_scale); if (!(sval = g_hash_table_lookup(scannerlist, key)) && (!scanlist || !(sval = g_hash_table_lookup(scanlist, key)))) { g_warning("`%s' not found", key); g_free(key); /* XXX */ *scale = val->hard_value; return gwy_si_unit_new(""); } *scale = val->hard_value*sval->hard_value; if (!sval->hard_value_units || !*sval->hard_value_units) { if (gwy_strequal(val->soft_scale, "Sens. Phase")) siunit = gwy_si_unit_new("deg"); else siunit = gwy_si_unit_new("V"); } else { siunit = gwy_si_unit_new_parse(sval->hard_value_units, &q); siunit2 = gwy_si_unit_new("V"); gwy_si_unit_multiply(siunit, siunit2, siunit); gwy_debug("Scale1 = %g V/LSB", val->hard_value); gwy_debug("Scale2 = %g %s", sval->hard_value, sval->hard_value_units); *scale *= pow10(q); gwy_debug("Total scale = %g %s/LSB", *scale, gwy_si_unit_get_string(siunit, GWY_SI_UNIT_FORMAT_PLAIN)); g_object_unref(siunit2); } g_free(key); return siunit; }
static GwyContainer* text_dump_import(gchar *buffer, gsize size, const gchar *filename, GError **error) { gchar *val, *key, *pos, *line, *title; GwyContainer *data; GwyDataField *dfield; gdouble xreal, yreal; gint xres, yres, id; GwySIUnit *uxy, *uz; const guchar *s; gdouble *d; gsize n; data = gwy_container_new(); pos = buffer; while ((line = gwy_str_next_line(&pos)) && *line) { val = strchr(line, '='); if (!val || *line != '/') { g_warning("Garbage key: %s", line); continue; } if ((gsize)(val - buffer) + 1 > size) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("End of file reached when value was expected.")); goto fail; } *val = '\0'; val++; if (!gwy_strequal(val, "[") || !pos || *pos != '[') { gwy_debug("<%s>=<%s>", line, val); if (*val) gwy_container_set_string_by_name(data, line, g_strdup(val)); else gwy_container_remove_by_name(data, line); continue; } g_assert(pos && *pos == '['); pos++; dfield = NULL; gwy_container_gis_object_by_name(data, line, &dfield); id = 0; sscanf(line, "/%d", &id); /* get datafield parameters from already read values, failing back * to values of original data field */ key = g_strconcat(line, "/xres", NULL); if (gwy_container_gis_string_by_name(data, key, &s)) xres = atoi(s); else if (dfield) xres = gwy_data_field_get_xres(dfield); else { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Missing data field width.")); goto fail; } g_free(key); key = g_strconcat(line, "/yres", NULL); if (gwy_container_gis_string_by_name(data, key, &s)) yres = atoi(s); else if (dfield) yres = gwy_data_field_get_yres(dfield); else { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Missing data field height.")); goto fail; } g_free(key); key = g_strconcat(line, "/xreal", NULL); if (gwy_container_gis_string_by_name(data, key, &s)) xreal = g_ascii_strtod(s, NULL); else if (dfield) xreal = gwy_data_field_get_xreal(dfield); else { g_warning("Missing real data field width."); xreal = 1.0; /* 0 could cause troubles */ } g_free(key); key = g_strconcat(line, "/yreal", NULL); if (gwy_container_gis_string_by_name(data, key, &s)) yreal = g_ascii_strtod(s, NULL); else if (dfield) yreal = gwy_data_field_get_yreal(dfield); else { g_warning("Missing real data field height."); yreal = 1.0; /* 0 could cause troubles */ } g_free(key); if (!(xres > 0 && yres > 0 && xreal > 0 && yreal > 0)) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Data field dimensions are not positive numbers.")); goto fail; } key = g_strconcat(line, "/unit-xy", NULL); if (gwy_container_gis_string_by_name(data, key, &s)) uxy = gwy_si_unit_new((const gchar*)s); else if (dfield) { uxy = gwy_data_field_get_si_unit_xy(dfield); uxy = gwy_si_unit_duplicate(uxy); } else { g_warning("Missing lateral units."); uxy = gwy_si_unit_new("m"); } g_free(key); key = g_strconcat(line, "/unit-z", NULL); if (gwy_container_gis_string_by_name(data, key, &s)) uz = gwy_si_unit_new((const gchar*)s); else if (dfield) { uz = gwy_data_field_get_si_unit_z(dfield); uz = gwy_si_unit_duplicate(uz); } else { g_warning("Missing value units."); uz = gwy_si_unit_new("m"); } g_free(key); key = g_strconcat(line, "/title", NULL); title = NULL; gwy_container_gis_string_by_name(data, key, (const guchar**)&title); /* We got the contained string but that would disappear. */ title = g_strdup(title); g_free(key); n = xres*yres*sizeof(gdouble); if ((gsize)(pos - buffer) + n + 3 > size) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("End of file reached inside a data field.")); goto fail; } dfield = GWY_DATA_FIELD(gwy_data_field_new(xres, yres, xreal, yreal, FALSE)); gwy_data_field_set_si_unit_xy(dfield, GWY_SI_UNIT(uxy)); gwy_object_unref(uxy); gwy_data_field_set_si_unit_z(dfield, GWY_SI_UNIT(uz)); gwy_object_unref(uz); d = gwy_data_field_get_data(dfield); #if (G_BYTE_ORDER == G_LITTLE_ENDIAN) memcpy(d, pos, n); #else gwy_memcpy_byte_swap(pos, (guint8*)d, sizeof(gdouble), xres*yres, sizeof(gdouble)-1); #endif pos += n; val = gwy_str_next_line(&pos); if (!gwy_strequal(val, "]]")) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Missing end of data field marker.")); gwy_object_unref(dfield); goto fail; } gwy_container_remove_by_prefix(data, line); gwy_container_set_object_by_name(data, line, dfield); g_object_unref(dfield); if (title) { key = g_strconcat(line, "/title", NULL); gwy_container_set_string_by_name(data, key, title); g_free(key); } gwy_file_channel_import_log_add(data, id, NULL, filename); } return data; fail: gwy_container_remove_by_prefix(data, NULL); g_object_unref(data); return NULL; }
static GwyContainer* gxsm_load(const gchar *filename, G_GNUC_UNUSED GwyRunType mode, GError **error) { static const gchar *dimensions[] = { "time", "value", "dimy", "dimx" }; GwyContainer *data = NULL; GwyDataField *dfield; GwySIUnit *siunit; NetCDF cdffile; const NetCDFDim *dim; const NetCDFVar *var; const NetCDFAttr *attr; gdouble real; gint i, power10; if (!cdffile_load(&cdffile, filename, error)) return NULL; if (cdffile.nrecs) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("NetCDF records are not supported.")); goto gxsm_load_fail; } /* Look for variable "H" or "FloatField". This seems to be how GXSM calls * data. */ if (!(var = cdffile_get_var(&cdffile, "H")) && !(var = cdffile_get_var(&cdffile, "FloatField"))) { err_NO_DATA(error); goto gxsm_load_fail; } /* Check the dimensions. We only know how to handle time=1 and value=1. */ for (i = 0; i < var->ndims; i++) { dim = cdffile.dims + var->dimids[i]; if (!gwy_strequal(dim->name, dimensions[i]) || (i < 2 && dim->length != 1)) { /* XXX */ err_NO_DATA(error); goto gxsm_load_fail; } } if (err_DIMENSION(error, cdffile.dims[var->dimids[3]].length) || err_DIMENSION(error, cdffile.dims[var->dimids[2]].length)) goto gxsm_load_fail; dfield = read_data_field((const guchar*)(cdffile.buffer + var->begin), cdffile.dims[var->dimids[3]].length, cdffile.dims[var->dimids[2]].length, var->type); if ((siunit = read_real_size(&cdffile, "rangex", &real, &power10))) { /* Use negated positive conditions to catch NaNs */ if (!((real = fabs(real)) > 0)) { g_warning("Real x size is 0.0, fixing to 1.0"); real = 1.0; } gwy_data_field_set_xreal(dfield, real*pow10(power10)); gwy_data_field_set_si_unit_xy(dfield, siunit); g_object_unref(siunit); } if ((siunit = read_real_size(&cdffile, "rangey", &real, &power10))) { /* Use negated positive conditions to catch NaNs */ if (!((real = fabs(real)) > 0)) { g_warning("Real y size is 0.0, fixing to 1.0"); real = 1.0; } gwy_data_field_set_yreal(dfield, real*pow10(power10)); /* must be the same gwy_data_field_set_si_unit_xy(dfield, siunit); */ g_object_unref(siunit); } if ((siunit = read_real_size(&cdffile, "rangez", &real, &power10))) { /* rangez seems to be some bogus value, take only units */ gwy_data_field_set_si_unit_z(dfield, siunit); gwy_data_field_multiply(dfield, pow10(power10)); g_object_unref(siunit); } if ((siunit = read_real_size(&cdffile, "dz", &real, &power10))) { /* on the other hand the units seem to be bogus here, take the range */ gwy_data_field_multiply(dfield, real); g_object_unref(siunit); } data = gwy_container_new(); gwy_container_set_object_by_name(data, "/0/data", dfield); g_object_unref(dfield); if ((attr = cdffile_get_attr(var->attrs, var->nattrs, "long_name")) && attr->type == NC_CHAR && attr->nelems) { gwy_container_set_string_by_name(data, "/0/data/title", g_strndup(attr->values, attr->nelems)); } gxsm_load_fail: gwy_file_abandon_contents(cdffile.buffer, cdffile.size, NULL); cdffile_free(&cdffile); return data; }
static gboolean read_file_info(const guchar *buffer, gsize size, FileInfo *info, GError **error) { guint expected_size, i; const guchar *p; gwy_clear(info, 1); p = buffer + MAGIC_SIZE; /* read structure variables from buffer */ for (i = 0; i < (HEADER_SIZE - MAGIC_SIZE)/8; i++) { gchar key[5]; guint32 value; key[4] = '\0'; memcpy(key, p, 4); p += 4; value = gwy_get_guint32_le(&p); if (!key[0]) continue; gwy_debug("%s: 0x%04x", key, value); /* Do not take values past the end of file and zeros into account at * all. The software seems to sometimes emit silly extra fields. */ if (!value || value >= size) continue; else if (gwy_strequal(key, "DESC")) info->desc_offset = value; else if (gwy_strequal(key, "DATE")) info->date_offset = value; else if (gwy_strequal(key, "PLET")) info->palette_offset = value; else if (gwy_strequal(key, "IMAG")) info->image_offset = value; else if (gwy_strequal(key, "HARD")) info->hard_offset = value; else if (gwy_strequal(key, "IMGP")) info->img_p_offset = value; else if (gwy_strequal(key, "SDES")) info->short_desc_offset = value; else if (gwy_strequal(key, "KEYS")) info->keys_offset = value; else { gwy_debug("Unknown field %s", key); } } /* Pixel image size */ if (!(p = get_param_pointer(buffer, size, info->image_offset, sizeof(guint16), "IMAG", error))) return FALSE; info->img_res = gwy_get_guint16_le(&p); if (err_DIMENSION(error, info->img_res)) return FALSE; /* Image data. It is the *same* pointer, just after the pixel size. */ info->image_data = (const guint16*)p; expected_size = (p - buffer) + info->img_res*info->img_res*sizeof(guint16); if (err_SIZE_MISMATCH(error, expected_size, size, FALSE)) return FALSE; /* Real image size */ if (!(p = get_param_pointer(buffer, size, info->hard_offset, sizeof(gfloat), "HARD", error))) return FALSE; info->real_size = gwy_get_gfloat_le(&p); if (!((info->real_size = fabs(info->real_size)) > 0)) { g_warning("Real size is 0.0, fixing to 1.0"); info->real_size = 1.0; } /* Value scale factor */ if (!(p = get_param_pointer(buffer, size, info->img_p_offset + 8, sizeof(gfloat), "IMGP", error))) return FALSE; info->z_scale = gwy_get_gfloat_le(&p); return TRUE; }
static guint find_data_offsets(const gchar *buffer, gsize size, GPtrArray *ezdfile, GError **error) { EZDSection *dataset, *section; GString *grkey; guint required_size = 0; gint ngroups, nchannels, i, j, k; guint ndata = 0; gchar *p; /* Sanity check */ if (!ezdfile->len) { err_NO_DATA(error); return 0; } dataset = (EZDSection*)g_ptr_array_index(ezdfile, 0); if (strcmp(dataset->name, "DataSet")) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("First section isn't DataSet")); return 0; } if (!(p = g_hash_table_lookup(dataset->meta, "GroupCount")) || (ngroups = atol(p)) <= 0) { err_INVALID(error, _("GroupCount in [DataSet]")); return 0; } /* Scan groups */ grkey = g_string_new(NULL); for (i = 0; i < ngroups; i++) { g_string_printf(grkey, "Gr%d-Count", i); if (!(p = g_hash_table_lookup(dataset->meta, grkey->str))) { g_warning("No count for group %u", i); continue; } if ((nchannels = atol(p)) <= 0) continue; /* Scan channels inside a group, note it's OK there's less channels * than specified */ for (j = 0; j < nchannels; j++) { g_string_printf(grkey, "Gr%d-Ch%d", i, j); if (!(p = g_hash_table_lookup(dataset->meta, grkey->str))) continue; section = NULL; for (k = 1; k < ezdfile->len; k++) { section = (EZDSection*)g_ptr_array_index(ezdfile, k); if (gwy_strequal(section->name, p)) break; } if (!section) { g_warning("Cannot find section for %s", p); continue; } /* Compute data position */ gwy_debug("Data %s at offset %u from data start", grkey->str, required_size); gwy_debug("xres = %d, yres = %d, bpp = %d, z-name = %s", section->xres, section->yres, section->bitdepth, section->zrange.name); if (section->yres < 2) { gwy_debug("Skipping 1D data Gr%d-Ch%d. FIXME.", i, j); continue; } ndata++; section->data = buffer + required_size; required_size += section->xres * section->yres * (section->bitdepth/8); if (required_size > size) { g_warning("Truncated file, %s doesn't fit", grkey->str); g_string_free(grkey, TRUE); section->data = NULL; return 0; } section->group = i; section->channel = j; } } g_string_free(grkey, TRUE); if (!ndata) err_NO_DATA(error); return ndata; }
static gboolean file_read_header(GPtrArray *ezdfile, gchar *buffer, GError **error) { EZDSection *section = NULL; gchar *p, *line; guint len; while ((line = gwy_str_next_line(&buffer))) { line = g_strstrip(line); if (!(len = strlen(line))) continue; if (line[0] == '[' && line[len-1] == ']') { section = g_new0(EZDSection, 1); g_ptr_array_add(ezdfile, section); line[len-1] = '\0'; section->name = g_strdup(line + 1); section->meta = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); gwy_debug("Section <%s>", section->name); continue; } if (!section) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Garbage before first header section.")); return FALSE; } /* Skip comments */ if (g_str_has_prefix(line, "--")) continue; p = strchr(line, '='); if (!p) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Malformed header line (missing =).")); return FALSE; } *p = '\0'; p++; if (gwy_strequal(line, "SaveMode")) { if (strcmp(p, "Binary")) g_warning("SaveMode is not Binary, this is not supported"); } else if (gwy_strequal(line, "SaveBits")) section->bitdepth = atol(p); else if (gwy_strequal(line, "SaveSign")) { section->sign = gwy_strequal(p, "Signed"); if (!section->sign) g_warning("SaveSign is not Signed, this is not supported"); } else if (gwy_strequal(line, "SaveOrder")) { if (gwy_strequal(p, "Intel")) section->byteorder = G_LITTLE_ENDIAN; else g_warning("SaveOrder is not Intel, this is not supported"); } else if (gwy_strequal(line, "Frame")) { if (gwy_strequal(p, "Scan forward")) section->direction = SCAN_FORWARD; else if (gwy_strequal(p, "Scan backward")) section->direction = SCAN_BACKWARD; } else if (gwy_strequal(line, "Points")) section->xres = atol(p); else if (gwy_strequal(line, "Lines")) section->yres = atol(p); /* FIXME: this is ugly, and incorrect for non-2D data */ else if (gwy_strequal(line, "Dim0Name")) section->xrange.name = g_strdup(p); else if (gwy_strequal(line, "Dim1Name")) section->yrange.name = g_strdup(p); else if (gwy_strequal(line, "Dim2Name")) section->zrange.name = g_strdup(p); else if (gwy_strequal(line, "Dim0Unit")) section->xrange.unit = g_strdup(p); else if (gwy_strequal(line, "Dim1Unit")) section->yrange.unit = g_strdup(p); else if (gwy_strequal(line, "Dim2Unit")) section->zrange.unit = g_strdup(p); else if (gwy_strequal(line, "Dim0Min")) section->xrange.min = g_ascii_strtod(p, NULL); else if (gwy_strequal(line, "Dim1Min")) section->yrange.min = g_ascii_strtod(p, NULL); else if (gwy_strequal(line, "Dim2Min")) section->zrange.min = g_ascii_strtod(p, NULL); else if (gwy_strequal(line, "Dim0Range")) section->xrange.range = g_ascii_strtod(p, NULL); else if (gwy_strequal(line, "Dim1Range")) section->yrange.range = g_ascii_strtod(p, NULL); else if (gwy_strequal(line, "Dim2Range")) section->zrange.range = g_ascii_strtod(p, NULL); else g_hash_table_replace(section->meta, g_strdup(line), g_strdup(p)); } return TRUE; }