static int dmg_open(BlockDriverState *bs, int flags)

{

    BDRVDMGState *s = bs->opaque;

    off_t info_begin,info_end,last_in_offset,last_out_offset;

    uint32_t count;

    uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;

    int64_t offset;



    bs->read_only = 1;

    s->n_chunks = 0;

    s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;



    /* read offset of info blocks */

    offset = bdrv_getlength(bs->file);

    if (offset < 0) {

        goto fail;

    }

    offset -= 0x1d8;



    info_begin = read_off(bs, offset);

    if (info_begin == 0) {

	goto fail;

    }



    if (read_uint32(bs, info_begin) != 0x100) {

        goto fail;

    }



    count = read_uint32(bs, info_begin + 4);

    if (count == 0) {

        goto fail;

    }

    info_end = info_begin + count;



    offset = info_begin + 0x100;



    /* read offsets */

    last_in_offset = last_out_offset = 0;

    while (offset < info_end) {

        uint32_t type;



	count = read_uint32(bs, offset);

	if(count==0)

	    goto fail;

        offset += 4;



	type = read_uint32(bs, offset);

	if (type == 0x6d697368 && count >= 244) {

	    int new_size, chunk_count;



            offset += 4;

            offset += 200;



	    chunk_count = (count-204)/40;

	    new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);

	    s->types = g_realloc(s->types, new_size/2);

	    s->offsets = g_realloc(s->offsets, new_size);

	    s->lengths = g_realloc(s->lengths, new_size);

	    s->sectors = g_realloc(s->sectors, new_size);

	    s->sectorcounts = g_realloc(s->sectorcounts, new_size);



	    for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {

		s->types[i] = read_uint32(bs, offset);

		offset += 4;

		if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {

		    if(s->types[i]==0xffffffff) {

			last_in_offset = s->offsets[i-1]+s->lengths[i-1];

			last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];

		    }

		    chunk_count--;

		    i--;

		    offset += 36;

		    continue;

		}

		offset += 4;



		s->sectors[i] = last_out_offset+read_off(bs, offset);

		offset += 8;



		s->sectorcounts[i] = read_off(bs, offset);

		offset += 8;



		s->offsets[i] = last_in_offset+read_off(bs, offset);

		offset += 8;



		s->lengths[i] = read_off(bs, offset);

		offset += 8;



		if(s->lengths[i]>max_compressed_size)

		    max_compressed_size = s->lengths[i];

		if(s->sectorcounts[i]>max_sectors_per_chunk)

		    max_sectors_per_chunk = s->sectorcounts[i];

	    }

	    s->n_chunks+=chunk_count;

	}

    }



    /* initialize zlib engine */

    s->compressed_chunk = g_malloc(max_compressed_size+1);

    s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk);

    if(inflateInit(&s->zstream) != Z_OK)

	goto fail;



    s->current_chunk = s->n_chunks;



    qemu_co_mutex_init(&s->lock);

    return 0;

fail:

    return -1;

}
