gint64
gst_aiur_stream_cache_read (GstAiurStreamCache * cache, guint64 size,
    char *buffer)
{
  gint64 readsize = -1;
  gint retrycnt = 0;
  if (cache == NULL) {
    return readsize;
  }

try_read:

  if (cache->closed == TRUE) {
    return readsize;
  }

  g_mutex_lock (cache->mutex);

  if (cache->seeking == TRUE)
    goto not_enough_bytes;

  if ((cache->threshold_max)
      && (cache->threshold_max < size + cache->threshold_pre)) {
    cache->threshold_max = size + cache->threshold_pre;
    /* enlarge maxsize means consumed */
    g_cond_signal (cache->consume_cond);
  }

  if (size > AVAIL_BYTES (cache)) {
    if (cache->eos) {           /* not enough bytes when eos */
      readsize = AVAIL_BYTES (cache);
      if (readsize) {
        READ_BYTES (cache, buffer, readsize);
      }
      goto beach;
    }
    goto not_enough_bytes;
  }

  readsize = size;
  READ_BYTES (cache, buffer, readsize);

  goto beach;


not_enough_bytes:
  //g_print("not enough %lld, try %d\n", size, retrycnt++);
  WAIT_COND_TIMEOUT (cache->produce_cond, cache->mutex, 1000000);
  g_mutex_unlock (cache->mutex);

  goto try_read;


beach:
  g_mutex_unlock (cache->mutex);
  return readsize;
}
Example #2
0
static VC_CONTAINER_STATUS_T rv9_read_frame_header(VC_CONTAINER_T *p_ctx)
{
   VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
   uint32_t seg_offset = (uint32_t) -1;
   uint8_t *buffer = module->data + FRAME_HEADER_LEN;

   if(READ_BYTES(p_ctx, module->data, FRAME_HEADER_LEN) != FRAME_HEADER_LEN) return VC_CONTAINER_ERROR_EOS;
   module->data_len = FRAME_HEADER_LEN;

   module->hdr.len = BI32(module->data);
   module->hdr.timestamp = BI32(module->data+4);
   module->hdr.sequence = BI16(module->data+8);
   module->hdr.flags = BI16(module->data+10);
   module->hdr.num_segments = BI32(module->data+16);

   module->frame_len = FRAME_HEADER_LEN + (module->hdr.num_segments * 8) + module->hdr.len;

   // if we have space, we store up the segments in memory so we can tell the frame
   // type, since most streams have their type byte as the first follow the segment information.
   // if we don't have space, then we just don't know the frame type, so will not emit timestamp
   // information as we don't know if it's reliable.
   if(module->hdr.num_segments <= MAX_NUM_SEGMENTS)
   {
      uint32_t i;

      if(READ_BYTES(p_ctx, buffer, 8*module->hdr.num_segments) != 8*module->hdr.num_segments) return VC_CONTAINER_ERROR_EOS;
      module->data_len += (module->hdr.num_segments * 8);

      for (i=0; i<module->hdr.num_segments; i++)
      {
         uint32_t valid_seg;
         uint32_t offset;

         valid_seg = BI32(buffer);
         offset = BI32(buffer+4);

         if (valid_seg && seg_offset > offset)
            seg_offset = offset;

         // this boolean field should have only 0 or 1 values
         if(valid_seg > 1) return VC_CONTAINER_ERROR_FORMAT_INVALID;

         buffer += 8;
      }
   }

   if(seg_offset == 0)
   {
      if (READ_BYTES(p_ctx, buffer, 1) != 1) return VC_CONTAINER_ERROR_EOS;
      module->data_len += 1;

      module->type = (*buffer >> 5) & 3;
   }
   else
Example #3
0
/**************************************************************************//**
 * Read/skip data from the container.
 * Can also be used to query information about the next block of data.
 *
 * @param p_ctx      The reader context.
 * @param p_packet   The container packet information, or NULL.
 * @param flags      The container read flags.
 * @return  The resulting status of the function.
 */
static VC_CONTAINER_STATUS_T rtp_reader_read( VC_CONTAINER_T *p_ctx,
                                               VC_CONTAINER_PACKET_T *p_packet,
                                               uint32_t flags )
{
   VC_CONTAINER_TRACK_T *track;
   VC_CONTAINER_TRACK_MODULE_T *t_module;
   VC_CONTAINER_STATUS_T status;

   if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && p_packet->track)
      return VC_CONTAINER_ERROR_INVALID_ARGUMENT;

   track = p_ctx->tracks[0];
   t_module = track->priv->module;

   CLEAR_BIT(t_module->flags, TRACK_NEW_PACKET);

   while (!BITS_AVAILABLE(p_ctx, &t_module->payload))
   {
      uint32_t bytes_read;

      /* No data left from last RTP packet, get another one */
      bytes_read = READ_BYTES(p_ctx, t_module->buffer, MAXIMUM_PACKET_SIZE);
      if (!bytes_read)
         return STREAM_STATUS(p_ctx);

      BITS_INIT(p_ctx, &t_module->payload, t_module->buffer, bytes_read);

      decode_rtp_packet_header(p_ctx, t_module);
      SET_BIT(t_module->flags, TRACK_NEW_PACKET);
   }

   if (p_packet)
   {
      uint32_t timestamp_top = t_module->timestamp >> 30;

      /* Determine whether timestamp has wrapped forwards or backwards around zero */
      if ((timestamp_top == 0) && (t_module->last_timestamp_top == 3))
         t_module->timestamp_wraps++;
      else if ((timestamp_top == 3) && (t_module->last_timestamp_top == 0))
         t_module->timestamp_wraps--;
      t_module->last_timestamp_top = timestamp_top;

      p_packet->dts = p_packet->pts = ((int64_t)t_module->timestamp_wraps << 32) | t_module->timestamp;
      p_packet->track = 0;
      p_packet->flags = 0;
   }

   status = t_module->payload_handler(p_ctx, track, p_packet, flags);
   if (p_packet && status == VC_CONTAINER_SUCCESS)
   {
      /* Adjust timestamps from RTP clock rate to microseconds */
      p_packet->pts = p_packet->pts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock;
      p_packet->dts = p_packet->dts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock;
   }

   STREAM_STATUS(p_ctx) = status;
   return status;
}
Example #4
0
void *readTask(FILE *inputFile){
	Task *task = createTask();
	TaskIdList *list = NULL;
	int endedTasks=-1, metaSize=-1, taskState=-1, id=-1;
	char *metadata = NULL;

	READ_BEGIN(inputFile);

	list = (TaskIdList *)readTaskIdList(inputFile);
	setTaskDependsOnMe(task, list);
	taskIdListDestroy(list);

	list = (TaskIdList *)readTaskIdList(inputFile);
	setTaskMyDeps(task, list);
	taskIdListDestroy(list);
	
	hashIntVoidDeserialize(inputFile, task->children);

	// make the children point to its mother
	HashIntVoidIterator *it = createHashIntVoidIterator(task->children, 0);
	PosHandlerIntVoid pos = hashIntVoidIteratorNext(it, task->children);
	while (pos != NULL) {
		Task *child = posGetValue(pos);
		child->mother = task;
		
		pos = hashIntVoidIteratorNext(it, task->children);
	}
	hashIntVoidIteratorDestroy(it, task->children);

	READ_NUM("id", id);
	setTaskId(task, id);

	READ_NUM("endedTasks", endedTasks);
	setTaskEndedTasks(task, endedTasks);

	READ_NUM("metaSize", metaSize);
	if (metaSize > 0) {
		metadata = malloc(metaSize);
	} else {
		metadata = NULL;	
	}
	READ_BYTES(inputFile, metadata, metaSize);
	setTaskMetadata(task, metadata, metaSize);
	if(metadata != NULL){
		free(metadata);
	}

	READ_NUM("taskState", taskState);
	setTaskState(task, taskState);
	
	DataSpace* dataSpace = createDataSpace();	
	readDataSpace(inputFile, dataSpace);

	setTaskDataSpace(task, dataSpace);
	
	READ_END
	return (void *)task;
}
Example #5
0
static VC_CONTAINER_STATUS_T ps_read_system_header( VC_CONTAINER_T *ctx )
{
   uint8_t header[8];
   uint32_t length;
   VC_CONTAINER_BITS_T bits;

   if(_READ_U32(ctx) != 0x1BB) return VC_CONTAINER_ERROR_CORRUPTED;
   LOG_FORMAT(ctx, "system_header");
   ctx->priv->module->level++;

   length = READ_U16(ctx, "header_length");
   if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED;
   if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS;

   BITS_INIT(ctx, &bits, header, 6);

   if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
   BITS_SKIP(ctx, &bits, 22, "rate_bound");
   if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
   BITS_SKIP(ctx, &bits, 6, "audio_bound");
   BITS_SKIP(ctx, &bits, 1, "fixed_flag");
   BITS_SKIP(ctx, &bits, 1, "CSPS_flag");
   BITS_SKIP(ctx, &bits, 1, "system_audio_lock_flag");
   BITS_SKIP(ctx, &bits, 1, "system_video_lock_flag");
   if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
   BITS_SKIP(ctx, &bits, 5, "video_bound");
   BITS_SKIP(ctx, &bits, 1, "packet_rate_restriction_flag");
   BITS_SKIP(ctx, &bits, 7, "reserved_bits");
   length -= 6;

   while(length >= 3 && (PEEK_U8(ctx) & 0x80))
   {
      SKIP_U8(ctx, "stream_id");
      SKIP_BYTES(ctx, 2);
      length -= 3;
   }
   SKIP_BYTES(ctx, length);

   ctx->priv->module->level--;
   return STREAM_STATUS(ctx);
}
Example #6
0
/*****************************************************************************
Functions exported as part of the Container Module API
 *****************************************************************************/
static VC_CONTAINER_STATUS_T binary_reader_read( VC_CONTAINER_T *p_ctx,
   VC_CONTAINER_PACKET_T *packet, uint32_t flags )
{
   VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
   unsigned int size;

   if(module->status != VC_CONTAINER_SUCCESS)
      return module->status;

   if(!module->block_size)
   {
      module->block_size = module->default_block_size;
      module->init = 0;
   }

   packet->size = module->block_size;
   packet->dts = packet->pts = VC_CONTAINER_TIME_UNKNOWN;
   if(module->init) packet->dts = packet->pts = 0;
   packet->track = 0;
   packet->flags = 0;

   if(flags & VC_CONTAINER_READ_FLAG_SKIP)
   {
      size = SKIP_BYTES(p_ctx, module->block_size);
      module->block_size -= size;
      module->status = STREAM_STATUS(p_ctx);
      return module->status;
   }

   if(flags & VC_CONTAINER_READ_FLAG_INFO)
      return VC_CONTAINER_SUCCESS;

   size = MIN(module->block_size, packet->buffer_size);
   size = READ_BYTES(p_ctx, packet->data, size);
   module->block_size -= size;
   packet->size = size;

   module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx);
   return module->status;
}
Example #7
0
static VC_CONTAINER_STATUS_T rv9_read_file_header(VC_CONTAINER_T *p_ctx,
   VC_CONTAINER_TRACK_T *track)
{
   VC_CONTAINER_STATUS_T status;
   VC_CONTAINER_FOURCC_T codec;
   uint8_t dummy[12];
   uint32_t length;

   if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS;

   length = BI32(dummy);
   if(length < 12 || length > 1024) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;

   if(dummy[4] != 'V' || dummy[5] != 'I' || dummy[6] != 'D' || dummy[7] != 'O' ||
      dummy[8] != 'R' || dummy[9] != 'V' ||                    dummy[11] != '0')
      return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;

   switch(dummy[10]) {
   case '4': codec = VC_CONTAINER_CODEC_RV40; break;
   case '3': codec = VC_CONTAINER_CODEC_RV30; break;
   case '2': codec = VC_CONTAINER_CODEC_RV20; break;
   case '1': codec = VC_CONTAINER_CODEC_RV10; break;
   default: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
   }

   if (!track)
      return VC_CONTAINER_SUCCESS;

   status = vc_container_track_allocate_extradata(p_ctx, track, length);
   if(status != VC_CONTAINER_SUCCESS) return status;

   if(READ_BYTES(p_ctx, track->format->extradata, length) != length) return VC_CONTAINER_ERROR_EOS;
   track->format->extradata_size = length;

   track->format->codec = codec;
   return STREAM_STATUS(p_ctx);
}
Example #8
0
void *readChildTask(FILE *inputFile){
	Task *task = createTask();
	TaskIdList *list = NULL;
	int metaSize = 0;
	char *metadata = NULL;
	int id = -1;

	READ_BEGIN(inputFile);

	list = (TaskIdList *)readTaskIdList(inputFile);
	setTaskDependsOnMe(task, list);
	taskIdListDestroy(list);

	list = (TaskIdList *)readTaskIdList(inputFile);
	setTaskMyDeps(task, list);
	taskIdListDestroy(list);

	READ_NUM("id", id);
	setTaskId(task, id);
	assert(id != -1);

	READ_NUM("metaSize", metaSize);
	if (metaSize > 0) {
		metadata = malloc(metaSize);
	} else {
		metadata = NULL;	
	}
	READ_BYTES(inputFile, metadata, metaSize);
	setTaskMetadata(task, metadata, metaSize);
	if(metadata != NULL){
		free(metadata);
	}
		
	READ_END
	return (void *)task;
}
Example #9
0
static struct manifest *
read_manifest(gzFile f)
{
	struct manifest *mf = create_empty_manifest();

	uint32_t magic;
	READ_INT(4, magic);
	if (magic != MAGIC) {
		cc_log("Manifest file has bad magic number %u", magic);
		goto error;
	}

	READ_BYTE(mf->version);
	if (mf->version != MANIFEST_VERSION) {
		cc_log("Manifest file has unknown version %u", mf->version);
		goto error;
	}

	READ_BYTE(mf->hash_size);
	if (mf->hash_size != 16) {
		// Temporary measure until we support different hash algorithms.
		cc_log("Manifest file has unsupported hash size %u", mf->hash_size);
		goto error;
	}

	READ_INT(2, mf->reserved);

	READ_INT(4, mf->n_files);
	mf->files = x_calloc(mf->n_files, sizeof(*mf->files));
	for (uint32_t i = 0; i < mf->n_files; i++) {
		READ_STR(mf->files[i]);
	}

	READ_INT(4, mf->n_file_infos);
	mf->file_infos = x_calloc(mf->n_file_infos, sizeof(*mf->file_infos));
	for (uint32_t i = 0; i < mf->n_file_infos; i++) {
		READ_INT(4, mf->file_infos[i].index);
		READ_BYTES(mf->hash_size, mf->file_infos[i].hash);
		READ_INT(4, mf->file_infos[i].size);
		READ_INT(8, mf->file_infos[i].mtime);
		READ_INT(8, mf->file_infos[i].ctime);
	}

	READ_INT(4, mf->n_objects);
	mf->objects = x_calloc(mf->n_objects, sizeof(*mf->objects));
	for (uint32_t i = 0; i < mf->n_objects; i++) {
		READ_INT(4, mf->objects[i].n_file_info_indexes);
		mf->objects[i].file_info_indexes =
			x_calloc(mf->objects[i].n_file_info_indexes,
			         sizeof(*mf->objects[i].file_info_indexes));
		for (uint32_t j = 0; j < mf->objects[i].n_file_info_indexes; j++) {
			READ_INT(4, mf->objects[i].file_info_indexes[j]);
		}
		READ_BYTES(mf->hash_size, mf->objects[i].hash.hash);
		READ_INT(4, mf->objects[i].hash.size);
	}

	return mf;

error:
	cc_log("Corrupt manifest file");
	free_manifest(mf);
	return NULL;
}
Example #10
0
static VC_CONTAINER_STATUS_T ps_read_pack_header( VC_CONTAINER_T *ctx )
{
   VC_CONTAINER_MODULE_T *module = ctx->priv->module;
   uint8_t header[10];
   int64_t scr, scr_base, scr_ext = INT64_C(0);
   uint64_t pack_offset = STREAM_POSITION(ctx);
   uint32_t mux_rate, stuffing;
   VC_CONTAINER_BITS_T bits;
   VC_CONTAINER_STATUS_T status;

   if(_READ_U32(ctx) != 0x1BA) return VC_CONTAINER_ERROR_CORRUPTED;
   LOG_FORMAT(ctx, "pack_header");
   
   module->level++;

   if (PEEK_U8(ctx) & 0x40)  /* program stream */
   {
      if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS;
      BITS_INIT(ctx, &bits, header, 10);
      if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30;
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15;
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]");
      LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base);
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      scr_ext = BITS_READ_U32(ctx, &bits, 9, "system_clock_reference_extension");
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate");
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      BITS_SKIP(ctx, &bits, 5, "reserved");
      stuffing = BITS_READ_U32(ctx, &bits, 3, "pack_stuffing_length");
      SKIP_BYTES(ctx, stuffing);
   }
   else /* system stream */
   {
      if(READ_BYTES(ctx, header, 8) != 8) return VC_CONTAINER_ERROR_EOS;
      BITS_INIT(ctx, &bits, header, 8);
      if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED;
      scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30;
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15;
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]");
      LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base);
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
      mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate");
      if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
   }

   if ((status = STREAM_STATUS(ctx)) != VC_CONTAINER_SUCCESS) return status;
   
   module->level--;

   /* Set or update system_clock_reference, adjust bias if necessary */
   scr = scr_base * INT64_C(300) + scr_ext;

   if (module->scr_offset == VC_CONTAINER_TIME_UNKNOWN)
      module->scr_offset = scr;

   if (module->scr == VC_CONTAINER_TIME_UNKNOWN)
      module->scr_bias = -scr;
   else if (scr < module->scr)
      module->scr_bias = module->scr - scr;

   if (module->scr != VC_CONTAINER_TIME_UNKNOWN)
   {
      /* system_clock_reference is not necessarily continuous across the entire stream */
      if (scr > module->scr)
      {
         int64_t data_rate;
         data_rate = INT64_C(27000000) * (pack_offset - module->pack_offset) / (scr - module->scr);

         if (module->data_rate)
         {
            /* Simple moving average over data rate seen so far */
            module->data_rate = (module->data_rate * 31 + data_rate) >> 5;
         }
         else
         {
tex_font_metrics_t* load_tex_font_metrics( char *filename )
{
    tex_font_metrics_t *tfm = NULL;
    FILE *tfm_file = NULL;
    int i;
    char magic[4];
    char *err_msg;
    int endian_check;
    bool_t swap_bytes;
    struct char_dims ch_dims;
    int num_chars;
    int texture_width, texture_height;
    char dummy;

    check_assertion( sizeof(int) == 4,
		     "This architecture's integer size is != 4" );
    check_assertion( sizeof(short) == 2,
		     "This architecture's short integer size is != 2" );
    check_assertion( sizeof(char) == 1,
		     "This architecture's char size is != 1" );

    /* Open file */
    tfm_file = fopen( filename, "rb" );
    if ( tfm_file == NULL ) {
	print_warning( MISSING_FILE_WARNING,
		       "Couldn't open font metrics file %s", filename );
	return NULL;
    }

    tfm = (tex_font_metrics_t*)malloc( sizeof(tex_font_metrics_t) );
    check_assertion( tfm != NULL, "out of memory" );

    /* Initialize tfm */
    for (i=0; i<MAX_TEX_FONT_CHARS; i++) {
	tfm->char_data[i] = NULL;
    }

    /* Check magic number */
    READ_BYTES( tfm_file, magic, sizeof(magic), False );

    if ( strncmp( magic, "\377tfm", 4 ) != 0 ) {
	err_msg = "File is not a valid tfm file";
	goto bail;
    }

    /* Check endian-ness */
    READ_BYTES( tfm_file, &endian_check, sizeof(int), False );

    if ( endian_check == 0x12345678 ) {
	swap_bytes = False;
    } else if ( endian_check == 0x78563412 ) {
	swap_bytes = True;
    } else {
	err_msg = "File is not a valid tfm file";
	goto bail;
    }

    /* Read in texture_width, texture_height, max_ascent, max_descent */
    READ_BYTES( tfm_file, &texture_width, sizeof(int), swap_bytes );
    READ_BYTES( tfm_file, &texture_height, sizeof(int), swap_bytes );
    READ_BYTES( tfm_file, &tfm->max_ascent, sizeof(int), swap_bytes );
    READ_BYTES( tfm_file, &tfm->max_descent, sizeof(int), swap_bytes );

    READ_BYTES( tfm_file, &num_chars, sizeof(int), swap_bytes );

    for (i=0; i<num_chars; i++) {
	tfm_char_data_t *cd;
	scalar_t sstep = 0.5/texture_width;
	scalar_t tstep = 0.5/texture_height;

	READ_BYTES( tfm_file, &ch_dims.ch, sizeof(unsigned short), swap_bytes );
	READ_BYTES( tfm_file, &ch_dims.w, sizeof(unsigned char), False );
	READ_BYTES( tfm_file, &ch_dims.h, sizeof(unsigned char), False );
	READ_BYTES( tfm_file, &ch_dims.x_offset, sizeof(char), False );
	READ_BYTES( tfm_file, &ch_dims.y_offset, sizeof(char), False );
	READ_BYTES( tfm_file, &ch_dims.kern_width, sizeof(char), False );
	READ_BYTES( tfm_file, &dummy, sizeof(char), False );
	READ_BYTES( tfm_file, &ch_dims.x_pixel, sizeof(short), swap_bytes );
	READ_BYTES( tfm_file, &ch_dims.y_pixel, sizeof(short), swap_bytes );

	if ( ch_dims.ch >= MAX_TEX_FONT_CHARS ) {
	    err_msg = "Two-byte characters are not supported";
	    goto bail;
	}

	cd = ( tfm_char_data_t * ) malloc( sizeof( tfm_char_data_t ) );

	check_assertion( cd != NULL, "out of memory" );

	cd->ll = make_point2d( ch_dims.x_offset, ch_dims.y_offset );
	cd->lr = make_point2d( cd->ll.x + ch_dims.w, cd->ll.y );
	cd->ur = make_point2d( cd->lr.x, cd->lr.y + ch_dims.h );
	cd->ul = make_point2d( cd->ur.x - ch_dims.w, cd->ur.y );

	cd->tex_ll = make_point2d( ch_dims.x_pixel / (scalar_t)texture_width + 
				   sstep,
				   ch_dims.y_pixel / (scalar_t)texture_height +
				   tstep );
	cd->tex_lr = make_point2d( cd->tex_ll.x + sstep +
				   ch_dims.w / (scalar_t)texture_width,
				   cd->tex_ll.y + tstep );
	cd->tex_ur = make_point2d( cd->tex_lr.x + sstep,
				   cd->tex_lr.y + tstep +
				   ch_dims.h / (scalar_t)texture_height );
	cd->tex_ul = make_point2d( cd->tex_ur.x + sstep - 
				   ch_dims.w / (scalar_t)texture_width,
				   cd->tex_ur.y + tstep );

	cd->kern_width = ch_dims.kern_width;

	tfm->char_data[ch_dims.ch] = cd;
    }

    fclose( tfm_file );

    return tfm;

bail:
    if ( tfm != NULL ) {
	for (i=0; i<MAX_TEX_FONT_CHARS; i++) {
	    if ( tfm->char_data[i] != NULL ) {
		free( tfm->char_data[i] );
	    }
	}
	free( tfm );
    }

    if ( tfm_file != NULL ) {
	fclose( tfm_file );
    }

    print_warning( IMPORTANT_WARNING, 
		   "Error opening font metrics file `%s': %s\n",
		   filename, err_msg );
    return NULL;
}