/*
*			GPAC - Multimedia Framework C SDK
*
*			Authors: Jean Le Feuvre
*			Copyright (c) Telecom ParisTech 2000-2012
*					All rights reserved
*
*  This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project
*
*  GPAC is free software; you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as published by
*  the Free Software Foundation; either version 2, or (at your option)
*  any later version.
*
*  GPAC is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; see the file COPYING.  If not, write to
*  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/

#include <gpac/internal/odf_dev.h>

#ifndef GPAC_MINIMAL_ODF


#define GF_IPMPX_DATA_ALLOC(__ptr, __stname, __tag) \
	__ptr = (__stname*)gf_malloc(sizeof(__stname)); \
	if (!__ptr) return NULL;	\
	memset(__ptr, 0, sizeof(__stname));	\
	((GF_IPMPX_Data *)__ptr)->tag = __tag;	\
	((GF_IPMPX_Data *)__ptr)->Version = 0x01;	\


#define GF_IPMPX_DELETE_ARRAY(__ar) if (__ar) { if (__ar->data) gf_free(__ar->data); gf_free(__ar); }

u32 get_field_size(u32 size_desc)
{
	if (size_desc < 0x00000080) return 1;
	else if (size_desc < 0x00004000) return 2;
	else if (size_desc < 0x00200000) return 3;
	else return 4;
}

void write_var_size(GF_BitStream *bs, u32 size)
{
	unsigned char vals[4];
	u32 length = size;

	vals[3] = (unsigned char)(length & 0x7f);
	length >>= 7;
	vals[2] = (unsigned char)((length & 0x7f) | 0x80);
	length >>= 7;
	vals[1] = (unsigned char)((length & 0x7f) | 0x80);
	length >>= 7;
	vals[0] = (unsigned char)((length & 0x7f) | 0x80);
	if (size < 0x00000080) {
		gf_bs_write_int(bs, vals[3], 8);
	}
	else if (size < 0x00004000) {
		gf_bs_write_int(bs, vals[2], 8);
		gf_bs_write_int(bs, vals[3], 8);
	}
	else if (size < 0x00200000) {
		gf_bs_write_int(bs, vals[1], 8);
		gf_bs_write_int(bs, vals[2], 8);
		gf_bs_write_int(bs, vals[3], 8);
	}
	else if (size < 0x10000000) {
		gf_bs_write_int(bs, vals[0], 8);
		gf_bs_write_int(bs, vals[1], 8);
		gf_bs_write_int(bs, vals[2], 8);
		gf_bs_write_int(bs, vals[3], 8);
	}
}

GF_IPMPX_ByteArray *GF_IPMPX_GetByteArray(GF_BitStream *bs)
{
	GF_IPMPX_ByteArray *ba;
	u32 val, size;
	size = 0;
	do {
		val = gf_bs_read_int(bs, 8);
		size <<= 7;
		size |= val & 0x7F;
	} while (val & 0x80);
	if (!size) return NULL;
	ba = (GF_IPMPX_ByteArray*)gf_malloc(sizeof(GF_IPMPX_ByteArray));
	ba->data = (char*)gf_malloc(sizeof(char)*size);
	gf_bs_read_data(bs, ba->data, size);
	ba->length = size;
	return ba;
}

u32 GF_IPMPX_GetByteArraySize(GF_IPMPX_ByteArray *ba)
{
	if (!ba) return 1;
	return ba->length + get_field_size(ba->length);
}

void GF_IPMPX_WriteByteArray(GF_BitStream *bs, GF_IPMPX_ByteArray *ba)
{
	if (!ba || !ba->data) {
		write_var_size(bs, 0);
	}
	else {
		write_var_size(bs, ba->length);
		gf_bs_write_data(bs, ba->data, ba->length);
	}
}

void GF_IPMPX_AUTH_Delete(GF_IPMPX_Authentication *auth)
{
	if (!auth) return;
	switch (auth->tag) {
	case GF_IPMPX_AUTH_AlgorithmDescr_Tag:
	{
		GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth;
		GF_IPMPX_DELETE_ARRAY(p->specAlgoID);
		GF_IPMPX_DELETE_ARRAY(p->OpaqueData);
		gf_free(p);
	}
	break;
	case GF_IPMPX_AUTH_KeyDescr_Tag:
	{
		GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth;
		if (p->keyBody) gf_free(p->keyBody);
		gf_free(p);
	}
	break;
	}
}

u32 GF_IPMPX_AUTH_Size(GF_IPMPX_Authentication *auth)
{
	u32 size;
	if (!auth) return 0;
	switch (auth->tag) {
	case GF_IPMPX_AUTH_AlgorithmDescr_Tag:
	{
		GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth;
		size = 1 + (p->specAlgoID ? GF_IPMPX_GetByteArraySize(p->specAlgoID) : 2);
		size += GF_IPMPX_GetByteArraySize(p->OpaqueData);
		return size;
	}
	break;
	case GF_IPMPX_AUTH_KeyDescr_Tag:
	{
		GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth;
		size = p->keyBodyLength;
		return size;
	}
	break;
	default:
		return 0;
	}
}

u32 GF_IPMPX_AUTH_FullSize(GF_IPMPX_Authentication *auth)
{
	u32 size = GF_IPMPX_AUTH_Size(auth);
	size += get_field_size(size);
	size += 1;/*tag*/
	return size;
}

GF_Err WriteGF_IPMPX_AUTH(GF_BitStream *bs, GF_IPMPX_Authentication *auth)
{
	u32 size;

	if (!auth) return GF_OK;
	gf_bs_write_int(bs, auth->tag, 8);

	size = GF_IPMPX_AUTH_Size(auth);
	write_var_size(bs, size);

	switch (auth->tag) {
	case GF_IPMPX_AUTH_AlgorithmDescr_Tag:
	{
		GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth;
		gf_bs_write_int(bs, p->specAlgoID ? 0 : 1, 1);
		gf_bs_write_int(bs, 0, 7);
		if (p->specAlgoID) {
			GF_IPMPX_WriteByteArray(bs, p->specAlgoID);
		}
		else {
			gf_bs_write_int(bs, p->regAlgoID, 16);
		}
		GF_IPMPX_WriteByteArray(bs, p->OpaqueData);
	}
	break;
	case GF_IPMPX_AUTH_KeyDescr_Tag:
	{
		GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth;
		/*tag*/
		gf_bs_write_data(bs, p->keyBody, p->keyBodyLength);
	}
	break;
	default:
		break;
	}
	return GF_OK;
}

GF_Err GF_IPMPX_AUTH_Parse(GF_BitStream *bs, GF_IPMPX_Authentication **auth)
{
	u32 val, size, tag;

	tag = gf_bs_read_int(bs, 8);
	size = 0;
	do {
		val = gf_bs_read_int(bs, 8);
		size <<= 7;
		size |= val & 0x7F;
	} while (val & 0x80);
	/*don't know if tolerated in IPMPX*/
	if (!size) return GF_OK;

	switch (tag) {
	case GF_IPMPX_AUTH_KeyDescr_Tag:
	{
		GF_IPMPX_AUTH_KeyDescriptor *p;
		GF_SAFEALLOC(p, GF_IPMPX_AUTH_KeyDescriptor);
		if (!p) return GF_OUT_OF_MEM;
		p->tag = tag;
		p->keyBodyLength = size;
		p->keyBody = (char*)gf_malloc(sizeof(char)*size);
		gf_bs_read_data(bs, p->keyBody, size);
		*auth = (GF_IPMPX_Authentication *)p;
		return GF_OK;
	}
	break;
	case GF_IPMPX_AUTH_AlgorithmDescr_Tag:
	{
		Bool isReg;
		GF_IPMPX_AUTH_AlgorithmDescriptor *p;
		GF_SAFEALLOC(p, GF_IPMPX_AUTH_AlgorithmDescriptor);
		if (!p) return GF_OUT_OF_MEM;
		p->tag = tag;
		isReg = (Bool)gf_bs_read_int(bs, 1);
		gf_bs_read_int(bs, 7);
		if (isReg) {
			p->regAlgoID = gf_bs_read_int(bs, 16);
		}
		else {
			p->specAlgoID = GF_IPMPX_GetByteArray(bs);
		}
		p->OpaqueData = GF_IPMPX_GetByteArray(bs);
		*auth = (GF_IPMPX_Authentication *)p;
		return GF_OK;
	}
	break;
	default:
		break;
	}
	return GF_NON_COMPLIANT_BITSTREAM;
}


GF_Err GF_IPMPX_ReadData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size);

u32 gf_ipmpx_data_full_size(GF_IPMPX_Data *p)
{
	u32 size;
	if (!p) return 0;
	size = gf_ipmpx_data_size(p);
	size += 5;	/*Version and dataID*/
	size += get_field_size(size);
	size += 1;/*tag*/
	return size;
}

GF_Err gf_ipmpx_data_parse(GF_BitStream *bs, GF_IPMPX_Data **out_data)
{
	GF_Err e;
	u32 val, size;
	u8 tag;
	GF_IPMPX_Data *p;

	*out_data = NULL;
	tag = gf_bs_read_int(bs, 8);
	size = 0;
	do {
		val = gf_bs_read_int(bs, 8);
		size <<= 7;
		size |= val & 0x7F;
	} while (val & 0x80);
	/*don't know if tolerated in IPMPX*/
	if (!size) return GF_OK;

	p = gf_ipmpx_data_new(tag);
	if (!p) return GF_NON_COMPLIANT_BITSTREAM;
	p->Version = gf_bs_read_int(bs, 8);
	p->dataID = gf_bs_read_int(bs, 32);
	e = GF_IPMPX_ReadData(bs, p, size);
	if (e) {
		gf_ipmpx_data_del(p);
		return e;
	}
	*out_data = p;
	return GF_OK;
}

GF_Err GF_IPMPX_WriteBase(GF_BitStream *bs, GF_IPMPX_Data *p)
{
	u32 size;

	if (!p) return GF_BAD_PARAM;
	size = gf_ipmpx_data_size(p);
	size += 5;	/*Version & dataID*/
	gf_bs_write_int(bs, p->tag, 8);
	write_var_size(bs, size);
	gf_bs_write_int(bs, p->Version, 8);
	gf_bs_write_int(bs, p->dataID, 32);
	return GF_OK;
}


static GF_IPMPX_Data *NewGF_IPMPX_InitAuthentication()
{
	GF_IPMPX_InitAuthentication *ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_InitAuthentication, GF_IPMPX_INIT_AUTHENTICATION_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_InitAuthentication(GF_IPMPX_Data *_p)
{
	GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p;
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_InitAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p;
	p->Context = gf_bs_read_int(bs, 32);
	p->AuthType = gf_bs_read_int(bs, 8);
	return GF_OK;
}
static u32 SizeGF_IPMPX_InitAuthentication(GF_IPMPX_Data *_p)
{
	return 5;
}
static GF_Err WriteGF_IPMPX_InitAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p;
	gf_bs_write_int(bs, p->Context, 32);
	gf_bs_write_int(bs, p->AuthType, 8);
	return GF_OK;
}


static GF_IPMPX_Data *NewGF_IPMPX_MutualAuthentication()
{
	GF_IPMPX_MutualAuthentication *ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_MutualAuthentication, GF_IPMPX_MUTUAL_AUTHENTICATION_TAG);
	ptr->candidateAlgorithms = gf_list_new();
	ptr->agreedAlgorithms = gf_list_new();
	ptr->certificates = gf_list_new();
	return (GF_IPMPX_Data *)ptr;
}

static void delete_algo_list(GF_List *algos)
{
	u32 i;
	for (i = 0; i<gf_list_count(algos); i++) {
		GF_IPMPX_Authentication *ip_auth = (GF_IPMPX_Authentication *)gf_list_get(algos, i);
		GF_IPMPX_AUTH_Delete(ip_auth);
	}
	gf_list_del(algos);
}
static void DelGF_IPMPX_MutualAuthentication(GF_IPMPX_Data *_p)
{
	GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p;
	delete_algo_list(p->candidateAlgorithms);
	delete_algo_list(p->agreedAlgorithms);
	GF_IPMPX_DELETE_ARRAY(p->AuthenticationData);
	GF_IPMPX_DELETE_ARRAY(p->opaque);
	GF_IPMPX_DELETE_ARRAY(p->authCodes);
	gf_ipmpx_data_del((GF_IPMPX_Data *)p->trustData);
	GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)p->publicKey);
	while (gf_list_count(p->certificates)) {
		GF_IPMPX_ByteArray *ba = (GF_IPMPX_ByteArray *)gf_list_get(p->certificates, 0);
		gf_list_rem(p->certificates, 0);
		GF_IPMPX_DELETE_ARRAY(ba);
	}
	gf_list_del(p->certificates);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_MutualAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_Err e;
	u32 i, count;
	Bool requestNegotiation, successNegotiation, inclAuthenticationData, inclAuthCodes;
	GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p;

	requestNegotiation = (Bool)gf_bs_read_int(bs, 1);
	successNegotiation = (Bool)gf_bs_read_int(bs, 1);
	p->failedNegotiation = (Bool)gf_bs_read_int(bs, 1);
	inclAuthenticationData = (Bool)gf_bs_read_int(bs, 1);
	inclAuthCodes = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 3);

	if (requestNegotiation) {
		count = gf_bs_read_int(bs, 8);
		for (i = 0; i<count; i++) {
			GF_IPMPX_Authentication *auth;
			e = GF_IPMPX_AUTH_Parse(bs, &auth);
			if (e) return e;
			gf_list_add(p->candidateAlgorithms, auth);
		}
	}
	if (successNegotiation) {
		count = gf_bs_read_int(bs, 8);
		for (i = 0; i<count; i++) {
			GF_IPMPX_Authentication *auth;
			e = GF_IPMPX_AUTH_Parse(bs, &auth);
			if (e) return e;
			gf_list_add(p->agreedAlgorithms, auth);
		}
	}
	if (inclAuthenticationData) p->AuthenticationData = GF_IPMPX_GetByteArray(bs);
	if (inclAuthCodes) {
		/*unspecified in spec, IM1 uses 8 bits*/
		u32 type = gf_bs_read_int(bs, 8);
		switch (type) {
		case 0x01:
			count = gf_bs_read_int(bs, 8);
			p->certType = gf_bs_read_int(bs, 32);
			for (i = 0; i<count; i++) {
				GF_IPMPX_ByteArray *ipd = GF_IPMPX_GetByteArray(bs);
				if (ipd) gf_list_add(p->certificates, ipd);
			}
			break;
		case 0x02:
			e = GF_IPMPX_AUTH_Parse(bs, (GF_IPMPX_Authentication**)&p->publicKey);
			if (e) return e;
			break;
		case 0xFE:
			p->opaque = GF_IPMPX_GetByteArray(bs);
			break;
		default:
			break;
		}
		e = gf_ipmpx_data_parse(bs, (GF_IPMPX_Data **)&p->trustData);
		if (e) return e;
		p->authCodes = GF_IPMPX_GetByteArray(bs);
	}
	return GF_OK;
}
static u32 SizeGF_IPMPX_MutualAuthentication(GF_IPMPX_Data *_p)
{
	u32 size, i, count;
	GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p;
	size = 1;
	count = gf_list_count(p->candidateAlgorithms);
	if (count) {
		size += 1;
		for (i = 0; i<count; i++) {
			GF_IPMPX_Authentication *ip_auth = (GF_IPMPX_Authentication *)gf_list_get(p->candidateAlgorithms, i);
			size += GF_IPMPX_AUTH_FullSize(ip_auth);
		}
	}
	count = gf_list_count(p->agreedAlgorithms);
	if (count) {
		size += 1;
		for (i = 0; i<count; i++) {
			GF_IPMPX_Authentication *ip_auth = (GF_IPMPX_Authentication *)gf_list_get(p->agreedAlgorithms, i);
			size += GF_IPMPX_AUTH_FullSize(ip_auth);
		}
	}
	if (p->AuthenticationData) size += GF_IPMPX_GetByteArraySize(p->AuthenticationData);

	count = gf_list_count(p->certificates);
	if (count || p->opaque || p->publicKey) {
		size += 1;
		/*type 1*/
		if (count) {
			size += 1 + 4;
			for (i = 0; i<count; i++) {
				GF_IPMPX_ByteArray *ba = (GF_IPMPX_ByteArray *)gf_list_get(p->certificates, i);
				size += GF_IPMPX_GetByteArraySize(ba);
			}
		}
		/*type 2*/
		else if (p->publicKey) {
			size += GF_IPMPX_AUTH_FullSize((GF_IPMPX_Authentication*)p->publicKey);
		}
		/*type 0xFE*/
		else if (p->opaque) {
			size += GF_IPMPX_GetByteArraySize(p->opaque);
		}
		size += gf_ipmpx_data_full_size((GF_IPMPX_Data *)p->trustData);
		size += GF_IPMPX_GetByteArraySize(p->authCodes);
	}
	return size;
}
static GF_Err WriteGF_IPMPX_MutualAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	u32 i, count;
	GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p;

	gf_bs_write_int(bs, gf_list_count(p->candidateAlgorithms) ? 1 : 0, 1);
	gf_bs_write_int(bs, gf_list_count(p->agreedAlgorithms) ? 1 : 0, 1);
	gf_bs_write_int(bs, p->failedNegotiation ? 1 : 0, 1);
	gf_bs_write_int(bs, p->AuthenticationData ? 1 : 0, 1);
	if (gf_list_count(p->certificates) || p->opaque || p->publicKey) {
		gf_bs_write_int(bs, 1, 1);
	}
	else {
		gf_bs_write_int(bs, 0, 1);
	}
	gf_bs_write_int(bs, 0, 3);

	count = gf_list_count(p->candidateAlgorithms);
	if (count) {
		gf_bs_write_int(bs, count, 8);
		for (i = 0; i<count; i++) {
			GF_IPMPX_Authentication *ip_auth = (GF_IPMPX_Authentication *)gf_list_get(p->candidateAlgorithms, i);
			WriteGF_IPMPX_AUTH(bs, ip_auth);
		}
	}
	count = gf_list_count(p->agreedAlgorithms);
	if (count) {
		gf_bs_write_int(bs, count, 8);
		for (i = 0; i<count; i++) {
			GF_IPMPX_Authentication *ip_auth = (GF_IPMPX_Authentication *)gf_list_get(p->agreedAlgorithms, i);
			WriteGF_IPMPX_AUTH(bs, ip_auth);
		}
	}
	if (p->AuthenticationData) GF_IPMPX_WriteByteArray(bs, p->AuthenticationData);

	count = gf_list_count(p->certificates);
	if (count || p->opaque || p->publicKey) {
		/*type 1*/
		if (count) {
			gf_bs_write_int(bs, 0x01, 8);

			gf_bs_write_int(bs, count, 8);
			gf_bs_write_int(bs, p->certType, 32);
			for (i = 0; i<count; i++) {
				GF_IPMPX_ByteArray *ipd = (GF_IPMPX_ByteArray *)gf_list_get(p->certificates, i);
				if (ipd) GF_IPMPX_WriteByteArray(bs, ipd);
			}
		}
		/*type 2*/
		else if (p->publicKey) {
			gf_bs_write_int(bs, 0x02, 8);
			WriteGF_IPMPX_AUTH(bs, (GF_IPMPX_Authentication *)p->publicKey);
		}
		/*type 0xFE*/
		else if (p->opaque) {
			gf_bs_write_int(bs, 0xFE, 8);
			GF_IPMPX_WriteByteArray(bs, p->opaque);
		}
		gf_ipmpx_data_write(bs, (GF_IPMPX_Data *)p->trustData);
		GF_IPMPX_WriteByteArray(bs, p->authCodes);
	}
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_TrustSecurityMetadata()
{
	GF_IPMPX_TrustSecurityMetadata *ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_TrustSecurityMetadata, GF_IPMPX_TRUST_SECURITY_METADATA_TAG);
	ptr->TrustedTools = gf_list_new();
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_TrustSecurityMetadata(GF_IPMPX_Data *_p)
{
	GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p;
	while (gf_list_count(p->TrustedTools)) {
		GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_list_get(p->TrustedTools, 0);
		gf_list_rem(p->TrustedTools, 0);
		while (gf_list_count(tt->trustSpecifications)) {
			GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_list_get(tt->trustSpecifications, 0);
			gf_list_rem(tt->trustSpecifications, 0);
			GF_IPMPX_DELETE_ARRAY(tts->CCTrustMetadata);
			gf_free(tts);
		}
		gf_list_del(tt->trustSpecifications);
		gf_free(tt);
	}
	gf_list_del(p->TrustedTools);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_TrustSecurityMetadata(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	u32 nbTools, nbSpec;
	GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p;
	nbTools = gf_bs_read_int(bs, 16);
	while (nbTools) {
		GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_malloc(sizeof(GF_IPMPX_TrustedTool));
		if (!tt) return GF_OUT_OF_MEM;
		memset(tt, 0, sizeof(GF_IPMPX_TrustedTool));
		tt->tag = GF_IPMPX_TRUSTED_TOOL_TAG;
		nbTools--;
		gf_list_add(p->TrustedTools, tt);
		gf_bs_read_data(bs, (char*)tt->toolID, 16);
		gf_bs_read_data(bs, tt->AuditDate, 5);
		tt->trustSpecifications = gf_list_new();
		nbSpec = gf_bs_read_int(bs, 16);
		while (nbSpec) {
			Bool has_cc;
			GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_malloc(sizeof(GF_IPMPX_TrustSpecification));
			if (!tts) return GF_OUT_OF_MEM;
			memset(tts, 0, sizeof(GF_IPMPX_TrustSpecification));
			tts->tag = GF_IPMPX_TRUST_SPECIFICATION_TAG;
			nbSpec--;
			gf_list_add(tt->trustSpecifications, tts);
			has_cc = (Bool)gf_bs_read_int(bs, 1);
			gf_bs_read_int(bs, 7);
			if (has_cc) {
				tts->CCTrustMetadata = GF_IPMPX_GetByteArray(bs);
			}
			else {
				gf_bs_read_data(bs, tts->startDate, 5);
				tts->attackerProfile = gf_bs_read_int(bs, 2);
				gf_bs_read_int(bs, 6);
				tts->trustedDuration = gf_bs_read_int(bs, 32);
			}
		}
	}
	return GF_OK;
}
static u32 SizeGF_IPMPX_TrustSecurityMetadata(GF_IPMPX_Data *_p)
{
	u32 size, i, j;
	GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p;
	size = 2;
	for (i = 0; i<gf_list_count(p->TrustedTools); i++) {
		GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_list_get(p->TrustedTools, i);
		size += 23;
		for (j = 0; j<gf_list_count(tt->trustSpecifications); j++) {
			GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_list_get(tt->trustSpecifications, j);
			size += 1;
			if (tts->CCTrustMetadata) size += GF_IPMPX_GetByteArraySize(tts->CCTrustMetadata);
			else size += 10;
		}
	}
	return size;
}
static GF_Err WriteGF_IPMPX_TrustSecurityMetadata(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	u32 i, j, c1, c2;
	GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p;

	c1 = gf_list_count(p->TrustedTools);
	gf_bs_write_int(bs, c1, 16);
	for (i = 0; i<c1; i++) {
		GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_list_get(p->TrustedTools, i);
		gf_bs_write_data(bs, (char*)tt->toolID, 16);
		gf_bs_write_data(bs, (char*)tt->AuditDate, 5);
		c2 = gf_list_count(tt->trustSpecifications);
		gf_bs_write_int(bs, c2, 16);

		for (j = 0; j<c2; j++) {
			GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_list_get(tt->trustSpecifications, j);
			gf_bs_write_int(bs, tts->CCTrustMetadata ? 1 : 0, 1);
			gf_bs_write_int(bs, 0, 7);
			if (tts->CCTrustMetadata) GF_IPMPX_WriteByteArray(bs, tts->CCTrustMetadata);
			else {
				gf_bs_write_data(bs, tts->startDate, 5);
				gf_bs_write_int(bs, tts->attackerProfile, 2);
				gf_bs_write_int(bs, 0, 6);
				gf_bs_write_int(bs, tts->trustedDuration, 32);
			}
		}
	}
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_SecureContainer()
{
	GF_IPMPX_SecureContainer *ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SecureContainer, GF_IPMPX_SECURE_CONTAINER_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_SecureContainer(GF_IPMPX_Data *_p)
{
	GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p;
	GF_IPMPX_DELETE_ARRAY(p->encryptedData);
	GF_IPMPX_DELETE_ARRAY(p->MAC);
	gf_ipmpx_data_del(p->protectedMsg);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_SecureContainer(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	Bool has_enc, has_mac;
	GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p;
	has_enc = (Bool)gf_bs_read_int(bs, 1);
	has_mac = (Bool)gf_bs_read_int(bs, 1);
	p->isMACEncrypted = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 5);
	if (has_enc) {
		p->encryptedData = GF_IPMPX_GetByteArray(bs);
		if (has_mac && !p->isMACEncrypted) p->MAC = GF_IPMPX_GetByteArray(bs);
	}
	else {
		GF_Err e = gf_ipmpx_data_parse(bs, &p->protectedMsg);
		if (e) return e;
		if (has_mac) p->MAC = GF_IPMPX_GetByteArray(bs);
	}
	return GF_OK;
}
static u32 SizeGF_IPMPX_SecureContainer(GF_IPMPX_Data *_p)
{
	u32 size = 1;
	GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p;
	if (p->MAC) p->isMACEncrypted = GF_FALSE;
	if (p->encryptedData) {
		size += GF_IPMPX_GetByteArraySize(p->encryptedData);
		if (p->MAC) size += GF_IPMPX_GetByteArraySize(p->MAC);
	}
	else {
		size += gf_ipmpx_data_full_size(p->protectedMsg);
		if (p->MAC) size += GF_IPMPX_GetByteArraySize(p->MAC);
	}
	return size;
}
static GF_Err WriteGF_IPMPX_SecureContainer(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p;
	if (p->MAC) p->isMACEncrypted = GF_FALSE;
	gf_bs_write_int(bs, p->encryptedData ? 1 : 0, 1);
	gf_bs_write_int(bs, (p->MAC || p->isMACEncrypted) ? 1 : 0, 1);
	gf_bs_write_int(bs, p->isMACEncrypted, 1);
	gf_bs_write_int(bs, 0, 5);
	if (p->encryptedData) {
		GF_IPMPX_WriteByteArray(bs, p->encryptedData);
		if (p->MAC) GF_IPMPX_WriteByteArray(bs, p->MAC);
	}
	else {
		GF_Err e = gf_ipmpx_data_write(bs, p->protectedMsg);
		if (e) return e;
		if (p->MAC) GF_IPMPX_WriteByteArray(bs, p->MAC);
	}
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_GetToolsResponse()
{
	GF_IPMPX_GetToolsResponse *ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolsResponse, GF_IPMPX_GET_TOOLS_RESPONSE_TAG);
	ptr->ipmp_tools = gf_list_new();
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_GetToolsResponse(GF_IPMPX_Data *_p)
{
	GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p;
	while (gf_list_count(p->ipmp_tools)) {
		/*IPMPTools are descriptors*/
		GF_Descriptor *d = (GF_Descriptor *)gf_list_get(p->ipmp_tools, 0);
		gf_list_rem(p->ipmp_tools, 0);
		gf_odf_desc_del((GF_Descriptor*)d);
	}
	gf_list_del(p->ipmp_tools);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_GetToolsResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	u32 NbBytes = 0;
	GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p;

	while (size>NbBytes) {
		u32 desc_size, start_o;
		GF_Descriptor *desc;
		GF_Err e;
		start_o = (u32)gf_bs_get_position(bs);
		e = gf_odf_parse_descriptor(bs, &desc, &desc_size);
		if (e) return e;
		gf_list_add(p->ipmp_tools, desc);
		NbBytes += (u32)gf_bs_get_position(bs) - start_o;
	}
	if (size<NbBytes) return GF_NON_COMPLIANT_BITSTREAM;
	return GF_OK;
}
static u32 SizeGF_IPMPX_GetToolsResponse(GF_IPMPX_Data *_p)
{
	u32 size = 0;
	GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p;
	gf_odf_size_descriptor_list(p->ipmp_tools, &size);
	return size;
}
static GF_Err WriteGF_IPMPX_GetToolsResponse(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	u32 i;
	GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p;

	for (i = 0; i<gf_list_count(p->ipmp_tools); i++) {
		GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(p->ipmp_tools, i);
		gf_odf_write_descriptor(bs, desc);
	}
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_ParametricDescription()
{
	GF_IPMPX_ParametricDescription *ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ParametricDescription, GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG);
	ptr->descriptions = gf_list_new();
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_ParametricDescription(GF_IPMPX_Data *_p)
{
	GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p;
	GF_IPMPX_DELETE_ARRAY(p->descriptionComment);
	while (gf_list_count(p->descriptions)) {
		GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, 0);
		gf_list_rem(p->descriptions, 0);
		GF_IPMPX_DELETE_ARRAY(it->main_class);
		GF_IPMPX_DELETE_ARRAY(it->subClass);
		GF_IPMPX_DELETE_ARRAY(it->typeData);
		GF_IPMPX_DELETE_ARRAY(it->type);
		GF_IPMPX_DELETE_ARRAY(it->addedData);
		gf_free(it);
	}
	gf_list_del(p->descriptions);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_ParametricDescription(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	u32 count;
	GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p;
	p->descriptionComment = GF_IPMPX_GetByteArray(bs);
	p->majorVersion = gf_bs_read_int(bs, 8);
	p->minorVersion = gf_bs_read_int(bs, 8);
	count = gf_bs_read_int(bs, 32);
	while (count) {
		GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_malloc(sizeof(GF_IPMPX_ParametricDescriptionItem));
		gf_list_add(p->descriptions, it);
		count--;
		it->main_class = GF_IPMPX_GetByteArray(bs);
		it->subClass = GF_IPMPX_GetByteArray(bs);
		it->typeData = GF_IPMPX_GetByteArray(bs);
		it->type = GF_IPMPX_GetByteArray(bs);
		it->addedData = GF_IPMPX_GetByteArray(bs);
	}
	return GF_OK;
}
static u32 SizeGF_IPMPX_ParametricDescription(GF_IPMPX_Data *_p)
{
	u32 size, i;
	GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p;
	size = GF_IPMPX_GetByteArraySize(p->descriptionComment);
	size += 6;
	for (i = 0; i<gf_list_count(p->descriptions); i++) {
		GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, i);
		size += GF_IPMPX_GetByteArraySize(it->main_class);
		size += GF_IPMPX_GetByteArraySize(it->subClass);
		size += GF_IPMPX_GetByteArraySize(it->typeData);
		size += GF_IPMPX_GetByteArraySize(it->type);
		size += GF_IPMPX_GetByteArraySize(it->addedData);
	}
	return size;
}
static GF_Err WriteGF_IPMPX_ParametricDescription(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	u32 i;
	GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p;
	GF_IPMPX_WriteByteArray(bs, p->descriptionComment);
	gf_bs_write_int(bs, p->majorVersion, 8);
	gf_bs_write_int(bs, p->minorVersion, 8);
	gf_bs_write_int(bs, gf_list_count(p->descriptions), 32);

	for (i = 0; i<gf_list_count(p->descriptions); i++) {
		GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, i);
		GF_IPMPX_WriteByteArray(bs, it->main_class);
		GF_IPMPX_WriteByteArray(bs, it->subClass);
		GF_IPMPX_WriteByteArray(bs, it->typeData);
		GF_IPMPX_WriteByteArray(bs, it->type);
		GF_IPMPX_WriteByteArray(bs, it->addedData);
	}
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_ToolParamCapabilitiesQuery()
{
	GF_IPMPX_ToolParamCapabilitiesQuery*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolParamCapabilitiesQuery, GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_ToolParamCapabilitiesQuery(GF_IPMPX_Data *_p)
{
	GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery *)_p;
	gf_ipmpx_data_del((GF_IPMPX_Data *)p->description);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_ToolParamCapabilitiesQuery(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p;
	return gf_ipmpx_data_parse(bs, (GF_IPMPX_Data **)&p->description);
}
static u32 SizeGF_IPMPX_ToolParamCapabilitiesQuery(GF_IPMPX_Data *_p)
{
	GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p;
	return gf_ipmpx_data_full_size((GF_IPMPX_Data *)p->description);
}
static GF_Err WriteGF_IPMPX_ToolParamCapabilitiesQuery(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p;
	return gf_ipmpx_data_write(bs, (GF_IPMPX_Data *)p->description);
}
static GF_IPMPX_Data *NewGF_IPMPX_ToolParamCapabilitiesResponse()
{
	GF_IPMPX_ToolParamCapabilitiesResponse*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolParamCapabilitiesResponse, GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_ToolParamCapabilitiesResponse(GF_IPMPX_Data *_p)
{
	gf_free(_p);
}
static GF_Err ReadGF_IPMPX_ToolParamCapabilitiesResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_IPMPX_ToolParamCapabilitiesResponse *p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p;
	p->capabilitiesSupported = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 7);
	return GF_OK;
}
static u32 SizeGF_IPMPX_ToolParamCapabilitiesResponse(GF_IPMPX_Data *_p)
{
	return 1;
}
static GF_Err WriteGF_IPMPX_ToolParamCapabilitiesResponse(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_ToolParamCapabilitiesResponse*p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p;
	gf_bs_write_int(bs, p->capabilitiesSupported, 1);
	gf_bs_write_int(bs, 0, 7);
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_ConnectTool()
{
	GF_IPMPX_ConnectTool*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ConnectTool, GF_IPMPX_CONNECT_TOOL_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_ConnectTool(GF_IPMPX_Data *_p)
{
	GF_IPMPX_ConnectTool *p = (GF_IPMPX_ConnectTool*)_p;
	if (p->toolDescriptor) gf_odf_desc_del((GF_Descriptor *)p->toolDescriptor);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_ConnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	u32 dsize;
	GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p;
	return gf_odf_parse_descriptor(bs, (GF_Descriptor **)&p->toolDescriptor, &dsize);
}
static u32 SizeGF_IPMPX_ConnectTool(GF_IPMPX_Data *_p)
{
	u32 size;
	GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p;
	gf_odf_size_descriptor((GF_Descriptor *)p->toolDescriptor, &size);
	size += gf_odf_size_field_size(size);
	return size;
}
static GF_Err WriteGF_IPMPX_ConnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p;
	gf_odf_write_descriptor(bs, (GF_Descriptor *)p->toolDescriptor);
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_DisconnectTool()
{
	GF_IPMPX_DisconnectTool*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_DisconnectTool, GF_IPMPX_DISCONNECT_TOOL_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_DisconnectTool(GF_IPMPX_Data *_p)
{
	gf_free(_p);
}
static GF_Err ReadGF_IPMPX_DisconnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p;
	p->IPMP_ToolContextID = gf_bs_read_int(bs, 32);
	return GF_OK;
}
static u32 SizeGF_IPMPX_DisconnectTool(GF_IPMPX_Data *_p)
{
	return 4;
}
static GF_Err WriteGF_IPMPX_DisconnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p;
	gf_bs_write_int(bs, p->IPMP_ToolContextID, 32);
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_GetToolContext()
{
	GF_IPMPX_GetToolContext*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolContext, GF_IPMPX_GET_TOOL_CONTEXT_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_GetToolContext(GF_IPMPX_Data *_p)
{
	gf_free(_p);
}
static GF_Err ReadGF_IPMPX_GetToolContext(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	Bool has_idex;
	GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p;
	p->scope = gf_bs_read_int(bs, 3);
	has_idex = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 4);
	if (has_idex) p->IPMP_DescriptorIDEx = gf_bs_read_int(bs, 16);

	return GF_OK;
}
static u32 SizeGF_IPMPX_GetToolContext(GF_IPMPX_Data *_p)
{
	GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p;
	return p->IPMP_DescriptorIDEx ? 3 : 1;
}
static GF_Err WriteGF_IPMPX_GetToolContext(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p;
	gf_bs_write_int(bs, p->scope, 3);
	gf_bs_write_int(bs, p->IPMP_DescriptorIDEx ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 4);
	if (p->IPMP_DescriptorIDEx) gf_bs_write_int(bs, p->IPMP_DescriptorIDEx, 16);
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_GetToolContextResponse()
{
	GF_IPMPX_GetToolContextResponse*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolContextResponse, GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_GetToolContextResponse(GF_IPMPX_Data *_p)
{
	gf_free(_p);
}
static GF_Err ReadGF_IPMPX_GetToolContextResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	Bool has_esid;
	GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p;
	has_esid = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 5);
	p->OD_ID = gf_bs_read_int(bs, 10);
	if (has_esid) p->ESD_ID = gf_bs_read_int(bs, 16);
	p->IPMP_ToolContextID = gf_bs_read_int(bs, 32);
	return GF_OK;
}
static u32 SizeGF_IPMPX_GetToolContextResponse(GF_IPMPX_Data *_p)
{
	GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p;
	return p->ESD_ID ? 8 : 6;
}
static GF_Err WriteGF_IPMPX_GetToolContextResponse(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p;
	gf_bs_write_int(bs, p->ESD_ID ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 5);
	gf_bs_write_int(bs, p->OD_ID, 10);
	if (p->ESD_ID) gf_bs_write_int(bs, p->ESD_ID, 16);
	gf_bs_write_int(bs, p->IPMP_ToolContextID, 32);
	return GF_OK;
}


static GF_IPMPX_Data *NewGF_IPMPX_AddToolNotificationListener()
{
	GF_IPMPX_AddToolNotificationListener*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_AddToolNotificationListener, GF_IPMPX_ADD_TOOL_LISTENER_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_AddToolNotificationListener(GF_IPMPX_Data *_p)
{
	gf_free(_p);
}
static GF_Err ReadGF_IPMPX_AddToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	u32 i;
	GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p;
	p->scope = gf_bs_read_int(bs, 3);
	gf_bs_read_int(bs, 5);
	p->eventTypeCount = gf_bs_read_int(bs, 8);
	for (i = 0; i<p->eventTypeCount; i++) p->eventType[i] = gf_bs_read_int(bs, 8);
	return GF_OK;
}
static u32 SizeGF_IPMPX_AddToolNotificationListener(GF_IPMPX_Data *_p)
{
	GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p;
	return p->eventTypeCount + 2;
}
static GF_Err WriteGF_IPMPX_AddToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	u32 i;
	GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p;
	gf_bs_write_int(bs, p->scope, 3);
	gf_bs_write_int(bs, 0, 5);
	gf_bs_write_int(bs, p->eventTypeCount, 8);
	for (i = 0; i<p->eventTypeCount; i++) gf_bs_write_int(bs, p->eventType[i], 8);
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_RemoveToolNotificationListener()
{
	GF_IPMPX_RemoveToolNotificationListener*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_RemoveToolNotificationListener, GF_IPMPX_REMOVE_TOOL_LISTENER_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_RemoveToolNotificationListener(GF_IPMPX_Data *_p)
{
	gf_free(_p);
}
static GF_Err ReadGF_IPMPX_RemoveToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	u32 i;
	GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p;
	p->eventTypeCount = gf_bs_read_int(bs, 8);
	for (i = 0; i<p->eventTypeCount; i++) p->eventType[i] = gf_bs_read_int(bs, 8);
	return GF_OK;
}
static u32 SizeGF_IPMPX_RemoveToolNotificationListener(GF_IPMPX_Data *_p)
{
	GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p;
	return p->eventTypeCount + 1;
}
static GF_Err WriteGF_IPMPX_RemoveToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	u32 i;
	GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p;
	gf_bs_write_int(bs, p->eventTypeCount, 8);
	for (i = 0; i<p->eventTypeCount; i++) gf_bs_write_int(bs, p->eventType[i], 8);
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_NotifyToolEvent()
{
	GF_IPMPX_NotifyToolEvent*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_NotifyToolEvent, GF_IPMPX_NOTIFY_TOOL_EVENT_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_NotifyToolEvent(GF_IPMPX_Data *_p)
{
	gf_free(_p);
}
static GF_Err ReadGF_IPMPX_NotifyToolEvent(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	Bool has_id;
	GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p;
	has_id = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 7);
	if (has_id) {
		p->OD_ID = gf_bs_read_int(bs, 10);
		gf_bs_read_int(bs, 6);
		p->ESD_ID = gf_bs_read_int(bs, 16);
	}
	p->eventType = gf_bs_read_int(bs, 8);
	p->IPMP_ToolContextID = gf_bs_read_int(bs, 32);
	return GF_OK;
}
static u32 SizeGF_IPMPX_NotifyToolEvent(GF_IPMPX_Data *_p)
{
	GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p;
	return (p->OD_ID || p->ESD_ID) ? 10 : 6;
}
static GF_Err WriteGF_IPMPX_NotifyToolEvent(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p;
	gf_bs_write_int(bs, (p->OD_ID || p->ESD_ID) ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 7);
	if (p->OD_ID || p->ESD_ID) {
		gf_bs_write_int(bs, p->OD_ID, 10);
		gf_bs_write_int(bs, 0, 6);
		gf_bs_write_int(bs, p->ESD_ID, 16);
	}
	gf_bs_write_int(bs, p->eventType, 8);
	gf_bs_write_int(bs, p->IPMP_ToolContextID, 32);
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_CanProcess()
{
	GF_IPMPX_CanProcess*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_CanProcess, GF_IPMPX_CAN_PROCESS_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_CanProcess(GF_IPMPX_Data *_p)
{
	gf_free(_p);
}
static GF_Err ReadGF_IPMPX_CanProcess(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p;
	p->canProcess = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 7);
	return GF_OK;
}
static u32 SizeGF_IPMPX_CanProcess(GF_IPMPX_Data *_p)
{
	return 1;
}
static GF_Err WriteGF_IPMPX_CanProcess(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p;
	gf_bs_write_int(bs, p->canProcess ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 7);
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_OpaqueData(u8 tag)
{
	GF_IPMPX_OpaqueData*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_OpaqueData, tag);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_OpaqueData(GF_IPMPX_Data *_p)
{
	GF_IPMPX_OpaqueData *p = (GF_IPMPX_OpaqueData*)_p;
	GF_IPMPX_DELETE_ARRAY(p->opaqueData);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_OpaqueData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p;
	p->opaqueData = GF_IPMPX_GetByteArray(bs);
	return GF_OK;
}
static u32 SizeGF_IPMPX_OpaqueData(GF_IPMPX_Data *_p)
{
	GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p;
	return GF_IPMPX_GetByteArraySize(p->opaqueData);
}
static GF_Err WriteGF_IPMPX_OpaqueData(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p;
	GF_IPMPX_WriteByteArray(bs, p->opaqueData);
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_KeyData()
{
	GF_IPMPX_KeyData*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_KeyData, GF_IPMPX_KEY_DATA_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_KeyData(GF_IPMPX_Data *_p)
{
	GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p;
	GF_IPMPX_DELETE_ARRAY(p->keyBody);
	GF_IPMPX_DELETE_ARRAY(p->OpaqueData);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_KeyData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p;
	p->keyBody = GF_IPMPX_GetByteArray(bs);
	p->flags = 0;
	if (gf_bs_read_int(bs, 1)) p->flags |= 1;
	if (gf_bs_read_int(bs, 1)) p->flags |= 1 << 1;
	if (gf_bs_read_int(bs, 1)) p->flags |= 1 << 2;
	if (gf_bs_read_int(bs, 1)) p->flags |= 1 << 3;
	gf_bs_read_int(bs, 4);
	if (p->flags & (1)) p->startDTS = gf_bs_read_long_int(bs, 64);
	if (p->flags & (1 << 1)) p->startPacketID = gf_bs_read_int(bs, 32);
	if (p->flags & (1 << 2)) p->expireDTS = gf_bs_read_long_int(bs, 64);
	if (p->flags & (1 << 3)) p->expirePacketID = gf_bs_read_int(bs, 32);
	p->OpaqueData = GF_IPMPX_GetByteArray(bs);
	return GF_OK;
}
static u32 SizeGF_IPMPX_KeyData(GF_IPMPX_Data *_p)
{
	u32 size = 0;
	GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p;
	size += GF_IPMPX_GetByteArraySize(p->keyBody);
	size += 1;
	if (p->flags & (1)) size += 8;
	if (p->flags & (1 << 1)) size += 4;
	if (p->flags & (1 << 2)) size += 8;
	if (p->flags & (1 << 3)) size += 4;
	size += GF_IPMPX_GetByteArraySize(p->OpaqueData);
	return size;
}
static GF_Err WriteGF_IPMPX_KeyData(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p;
	GF_IPMPX_WriteByteArray(bs, p->keyBody);
	gf_bs_write_int(bs, (p->flags & (1)) ? 1 : 0, 1);
	gf_bs_write_int(bs, (p->flags & (1 << 1)) ? 1 : 0, 1);
	gf_bs_write_int(bs, (p->flags & (1 << 2)) ? 1 : 0, 1);
	gf_bs_write_int(bs, (p->flags & (1 << 3)) ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 4);
	if (p->flags & (1)) gf_bs_write_long_int(bs, p->startDTS, 64);
	if (p->flags & (1 << 1)) gf_bs_write_int(bs, p->startPacketID, 32);
	if (p->flags & (1 << 2)) gf_bs_write_long_int(bs, p->expireDTS, 64);
	if (p->flags & (1 << 3)) gf_bs_write_int(bs, p->expirePacketID, 32);
	GF_IPMPX_WriteByteArray(bs, p->OpaqueData);
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_SelectiveDecryptionInit()
{
	GF_IPMPX_SelectiveDecryptionInit*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SelectiveDecryptionInit, GF_IPMPX_SEL_DEC_INIT_TAG);
	ptr->SelEncBuffer = gf_list_new();
	ptr->SelEncFields = gf_list_new();
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_SelectiveDecryptionInit(GF_IPMPX_Data *_p)
{
	GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p;
	while (gf_list_count(p->SelEncBuffer)) {
		GF_IPMPX_SelEncBuffer *sb = (GF_IPMPX_SelEncBuffer *)gf_list_get(p->SelEncBuffer, 0);
		gf_list_rem(p->SelEncBuffer, 0);
		GF_IPMPX_DELETE_ARRAY(sb->Stream_Cipher_Specific_Init_Info);
		gf_free(sb);
	}
	gf_list_del(p->SelEncBuffer);
	while (gf_list_count(p->SelEncFields)) {
		GF_IPMPX_SelEncField*sf = (GF_IPMPX_SelEncField*)gf_list_get(p->SelEncFields, 0);
		gf_list_rem(p->SelEncFields, 0);
		GF_IPMPX_DELETE_ARRAY(sf->shuffleSpecificInfo);
		if (sf->mappingTable) gf_free(sf->mappingTable);
		gf_free(sf);
	}
	gf_list_del(p->SelEncFields);
	if (p->RLE_Data) gf_free(p->RLE_Data);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_SelectiveDecryptionInit(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	u32 count, i;
	Bool is_spec;
	GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p;
	p->mediaTypeExtension = gf_bs_read_int(bs, 8);
	p->mediaTypeIndication = gf_bs_read_int(bs, 8);
	p->profileLevelIndication = gf_bs_read_int(bs, 8);
	p->compliance = gf_bs_read_int(bs, 8);
	count = gf_bs_read_int(bs, 8);
	while (count) {
		Bool is_block;
		GF_IPMPX_SelEncBuffer *sb;
		GF_SAFEALLOC(sb, GF_IPMPX_SelEncBuffer);
		if (!sb) return GF_OUT_OF_MEM;

		gf_list_add(p->SelEncBuffer, sb);
		count--;
		gf_bs_read_data(bs, (char*)sb->cipher_Id, 16);
		sb->syncBoundary = gf_bs_read_int(bs, 8);
		is_block = (Bool)gf_bs_read_int(bs, 1);
		gf_bs_read_int(bs, 7);
		if (is_block) {
			sb->mode = gf_bs_read_int(bs, 8);
			sb->blockSize = gf_bs_read_int(bs, 16);
			sb->keySize = gf_bs_read_int(bs, 16);
		}
		else {
			sb->Stream_Cipher_Specific_Init_Info = GF_IPMPX_GetByteArray(bs);
		}
	}
	is_spec = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 7);
	if (is_spec) {
		Bool is_map;
		count = gf_bs_read_int(bs, 8);
		while (count) {
			GF_IPMPX_SelEncField *sf;
			GF_SAFEALLOC(sf, GF_IPMPX_SelEncField);
			if (!sf) return GF_OUT_OF_MEM;

			gf_list_add(p->SelEncFields, sf);
			count--;
			sf->field_Id = gf_bs_read_int(bs, 8);
			sf->field_Scope = gf_bs_read_int(bs, 3);
			gf_bs_read_int(bs, 5);
			sf->buf = gf_bs_read_int(bs, 8);
			is_map = (Bool)gf_bs_read_int(bs, 1);
			gf_bs_read_int(bs, 7);
			if (is_map) {
				Bool sendMapTable = (Bool)gf_bs_read_int(bs, 1);
				Bool isShuffled = (Bool)gf_bs_read_int(bs, 1);
				gf_bs_read_int(bs, 6);
				if (sendMapTable) {
					sf->mappingTableSize = gf_bs_read_int(bs, 16);
					sf->mappingTable = (u16*)gf_malloc(sizeof(u16) * sf->mappingTableSize);
					for (i = 0; i<sf->mappingTableSize; i++) sf->mappingTable[i] = gf_bs_read_int(bs, 16);
				}
				if (isShuffled) sf->shuffleSpecificInfo = GF_IPMPX_GetByteArray(bs);
			}
		}
	}
	else {
		p->RLE_DataLength = gf_bs_read_int(bs, 16);
		p->RLE_Data = (u16*)gf_malloc(sizeof(u16)*p->RLE_DataLength);
		for (i = 0; i<p->RLE_DataLength; i++) p->RLE_Data[i] = gf_bs_read_int(bs, 16);
	}
	return GF_OK;
}
static u32 SizeGF_IPMPX_SelectiveDecryptionInit(GF_IPMPX_Data *_p)
{
	u32 size, i;
	GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p;

	size = 5;
	for (i = 0; i<gf_list_count(p->SelEncBuffer); i++) {
		GF_IPMPX_SelEncBuffer *sb = (GF_IPMPX_SelEncBuffer *)gf_list_get(p->SelEncBuffer, i);
		size += 18;
		if (sb->Stream_Cipher_Specific_Init_Info) {
			size += GF_IPMPX_GetByteArraySize(sb->Stream_Cipher_Specific_Init_Info);
		}
		else {
			size += 5;
		}
	}
	size += 1;
	if (p->RLE_Data) {
		size += 2 + 2 * p->RLE_DataLength;
	}
	else {
		size += 1;
		for (i = 0; i<gf_list_count(p->SelEncFields); i++) {
			GF_IPMPX_SelEncField *sf = (GF_IPMPX_SelEncField *)gf_list_get(p->SelEncFields, i);
			size += 4;
			if (sf->mappingTable || sf->shuffleSpecificInfo) {
				size += 1;
				if (sf->mappingTable) size += 2 + 2 * sf->mappingTableSize;
				if (sf->shuffleSpecificInfo) size += GF_IPMPX_GetByteArraySize(sf->shuffleSpecificInfo);
			}
		}
	}
	return size;
}
static GF_Err WriteGF_IPMPX_SelectiveDecryptionInit(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	u32 count, i, j;
	GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p;

	gf_bs_write_int(bs, p->mediaTypeExtension, 8);
	gf_bs_write_int(bs, p->mediaTypeIndication, 8);
	gf_bs_write_int(bs, p->profileLevelIndication, 8);
	gf_bs_write_int(bs, p->compliance, 8);

	count = gf_list_count(p->SelEncBuffer);
	gf_bs_write_int(bs, count, 8);
	for (i = 0; i<count; i++) {
		GF_IPMPX_SelEncBuffer *sb = (GF_IPMPX_SelEncBuffer *)gf_list_get(p->SelEncBuffer, i);
		gf_bs_write_data(bs, (char*)sb->cipher_Id, 16);
		gf_bs_write_int(bs, sb->syncBoundary, 8);
		gf_bs_write_int(bs, sb->Stream_Cipher_Specific_Init_Info ? 0 : 1, 1);
		gf_bs_write_int(bs, 0, 7);
		if (sb->Stream_Cipher_Specific_Init_Info) {
			GF_IPMPX_WriteByteArray(bs, sb->Stream_Cipher_Specific_Init_Info);
		}
		else {
			gf_bs_write_int(bs, sb->mode, 8);
			gf_bs_write_int(bs, sb->blockSize, 16);
			gf_bs_write_int(bs, sb->keySize, 16);
		}
	}
	gf_bs_write_int(bs, p->RLE_Data ? 0 : 1, 1);
	gf_bs_write_int(bs, 0, 7);
	if (p->RLE_Data) {
		gf_bs_write_int(bs, p->RLE_DataLength, 16);
		for (i = 0; i<p->RLE_DataLength; i++) gf_bs_write_int(bs, p->RLE_Data[i], 16);
	}
	else {
		count = gf_list_count(p->SelEncFields);
		gf_bs_write_int(bs, count, 8);
		for (i = 0; i<count; i++) {
			GF_IPMPX_SelEncField *sf = (GF_IPMPX_SelEncField *)gf_list_get(p->SelEncFields, i);
			gf_bs_write_int(bs, sf->field_Id, 8);
			gf_bs_write_int(bs, sf->field_Scope, 3);
			gf_bs_write_int(bs, 0, 5);
			gf_bs_write_int(bs, sf->buf, 8);
			gf_bs_write_int(bs, (sf->mappingTable || sf->shuffleSpecificInfo) ? 1 : 0, 1);
			gf_bs_write_int(bs, 0, 7);
			if (sf->mappingTable || sf->shuffleSpecificInfo) {
				gf_bs_write_int(bs, sf->mappingTable ? 1 : 0, 1);
				gf_bs_write_int(bs, sf->shuffleSpecificInfo ? 1 : 0, 1);
				gf_bs_write_int(bs, 0, 6);
				if (sf->mappingTable) {
					gf_bs_write_int(bs, sf->mappingTableSize, 16);
					for (j = 0; j<sf->mappingTableSize; j++) gf_bs_write_int(bs, sf->mappingTable[j], 16);
				}
				if (sf->shuffleSpecificInfo) GF_IPMPX_WriteByteArray(bs, sf->shuffleSpecificInfo);
			}
		}
	}
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_WatermarkingInit(u8 tag)
{
	GF_IPMPX_WatermarkingInit *ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_WatermarkingInit, tag);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_WatermarkingInit(GF_IPMPX_Data *_p)
{
	GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p;
	if (p->wmPayload) gf_free(p->wmPayload);
	if (p->opaqueData) gf_free(p->opaqueData);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_WatermarkingInit(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	Bool has_opaque_data;
	GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p;

	p->inputFormat = gf_bs_read_int(bs, 8);
	p->requiredOp = gf_bs_read_int(bs, 4);
	has_opaque_data = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 3);
	if (p->inputFormat == 0x01) {
		if (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) {
			p->nChannels = gf_bs_read_int(bs, 8);
			p->bitPerSample = gf_bs_read_int(bs, 8);
			p->frequency = gf_bs_read_int(bs, 32);
		}
		else {
			p->frame_horizontal_size = gf_bs_read_int(bs, 16);
			p->frame_vertical_size = gf_bs_read_int(bs, 16);
			p->chroma_format = gf_bs_read_int(bs, 8);
		}
	}
	switch (p->requiredOp) {
	case GF_IPMPX_WM_INSERT:
	case GF_IPMPX_WM_REMARK:
		p->wmPayloadLen = gf_bs_read_int(bs, 16);
		p->wmPayload = (char*)gf_malloc(sizeof(u8) * p->wmPayloadLen);
		gf_bs_read_data(bs, p->wmPayload, p->wmPayloadLen);
		break;
	case GF_IPMPX_WM_EXTRACT:
	case GF_IPMPX_WM_DETECT_COMPRESSION:
		p->wmRecipientId = gf_bs_read_int(bs, 16);
		break;
	}
	if (has_opaque_data) {
		p->opaqueDataSize = gf_bs_read_int(bs, 16);
		p->opaqueData = (char*)gf_malloc(sizeof(u8) * p->wmPayloadLen);
		gf_bs_read_data(bs, p->opaqueData, p->opaqueDataSize);
	}
	return GF_OK;
}
static u32 SizeGF_IPMPX_WatermarkingInit(GF_IPMPX_Data *_p)
{
	u32 size;
	GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p;
	size = 2;
	if (p->inputFormat == 0x01) size += (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) ? 6 : 5;
	switch (p->requiredOp) {
	case GF_IPMPX_WM_INSERT:
	case GF_IPMPX_WM_REMARK:
		size += 2 + p->wmPayloadLen;
		break;
	case GF_IPMPX_WM_EXTRACT:
	case GF_IPMPX_WM_DETECT_COMPRESSION:
		size += 2;
		break;
	}
	if (p->opaqueData) size += p->opaqueDataSize + 2;
	return size;
}
static GF_Err WriteGF_IPMPX_WatermarkingInit(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_WatermarkingInit*p = (GF_IPMPX_WatermarkingInit*)_p;

	gf_bs_write_int(bs, p->inputFormat, 8);
	gf_bs_write_int(bs, p->requiredOp, 4);
	gf_bs_write_int(bs, p->opaqueData ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 3);
	if (p->inputFormat == 0x01) {
		if (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) {
			gf_bs_write_int(bs, p->nChannels, 8);
			gf_bs_write_int(bs, p->bitPerSample, 8);
			gf_bs_write_int(bs, p->frequency, 32);
		}
		else {
			gf_bs_write_int(bs, p->frame_horizontal_size, 16);
			gf_bs_write_int(bs, p->frame_vertical_size, 16);
			gf_bs_write_int(bs, p->chroma_format, 8);
		}
	}
	switch (p->requiredOp) {
	case GF_IPMPX_WM_INSERT:
	case GF_IPMPX_WM_REMARK:
		gf_bs_write_int(bs, p->wmPayloadLen, 16);
		gf_bs_write_data(bs, p->wmPayload, p->wmPayloadLen);
		break;
	case GF_IPMPX_WM_EXTRACT:
	case GF_IPMPX_WM_DETECT_COMPRESSION:
		gf_bs_write_int(bs, p->wmRecipientId, 16);
		break;
	}
	if (p->opaqueData) {
		gf_bs_write_int(bs, p->opaqueDataSize, 16);
		gf_bs_write_data(bs, p->opaqueData, p->opaqueDataSize);
	}
	return GF_OK;
}
static GF_IPMPX_Data *NewGF_IPMPX_SendWatermark(u8 tag)
{
	GF_IPMPX_SendWatermark*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SendWatermark, tag);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_SendWatermark(GF_IPMPX_Data *_p)
{
	GF_IPMPX_SendWatermark*p = (GF_IPMPX_SendWatermark*)_p;
	GF_IPMPX_DELETE_ARRAY(p->payload);
	GF_IPMPX_DELETE_ARRAY(p->opaqueData);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_SendWatermark(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	Bool has_op_data;
	GF_IPMPX_SendWatermark *p = (GF_IPMPX_SendWatermark*)_p;
	p->wm_status = gf_bs_read_int(bs, 2);
	p->compression_status = gf_bs_read_int(bs, 2);
	has_op_data = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 3);
	if (p->wm_status == GF_IPMPX_WM_PAYLOAD) p->payload = GF_IPMPX_GetByteArray(bs);
	if (has_op_data) p->opaqueData = GF_IPMPX_GetByteArray(bs);
	return GF_OK;
}
static u32 SizeGF_IPMPX_SendWatermark(GF_IPMPX_Data *_p)
{
	u32 size;
	GF_IPMPX_SendWatermark *p = (GF_IPMPX_SendWatermark*)_p;
	size = 1;
	if (p->payload) size += GF_IPMPX_GetByteArraySize(p->payload);
	if (p->opaqueData) size += GF_IPMPX_GetByteArraySize(p->opaqueData);
	return size;
}
static GF_Err WriteGF_IPMPX_SendWatermark(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_SendWatermark*p = (GF_IPMPX_SendWatermark*)_p;
	if (p->payload) p->wm_status = GF_IPMPX_WM_PAYLOAD;
	gf_bs_write_int(bs, p->wm_status, 2);
	gf_bs_write_int(bs, p->compression_status, 2);
	gf_bs_write_int(bs, p->opaqueData ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 3);
	if (p->payload) GF_IPMPX_WriteByteArray(bs, p->payload);
	if (p->opaqueData) GF_IPMPX_WriteByteArray(bs, p->opaqueData);
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_ToolAPI_Config()
{
	GF_IPMPX_ToolAPI_Config*ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolAPI_Config, GF_IPMPX_TOOL_API_CONFIG_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_ToolAPI_Config(GF_IPMPX_Data *_p)
{
	GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p;
	GF_IPMPX_DELETE_ARRAY(p->opaqueData);
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_ToolAPI_Config(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	Bool has_i, has_m;
	GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p;
	has_i = (Bool)gf_bs_read_int(bs, 1);
	has_m = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 6);
	if (has_i) p->Instantiation_API_ID = gf_bs_read_int(bs, 32);
	if (has_m) p->Messaging_API_ID = gf_bs_read_int(bs, 32);
	p->opaqueData = GF_IPMPX_GetByteArray(bs);
	return GF_OK;
}
static u32 SizeGF_IPMPX_ToolAPI_Config(GF_IPMPX_Data *_p)
{
	u32 size;
	GF_IPMPX_ToolAPI_Config *p = (GF_IPMPX_ToolAPI_Config*)_p;
	size = 1;
	if (p->Instantiation_API_ID) size += 4;
	if (p->Messaging_API_ID) size += 4;
	size += GF_IPMPX_GetByteArraySize(p->opaqueData);
	return size;
}
static GF_Err WriteGF_IPMPX_ToolAPI_Config(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p;
	gf_bs_write_int(bs, p->Instantiation_API_ID ? 1 : 0, 1);
	gf_bs_write_int(bs, p->Messaging_API_ID ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 6);
	if (p->Instantiation_API_ID) gf_bs_write_int(bs, p->Instantiation_API_ID, 32);
	if (p->Messaging_API_ID) gf_bs_write_int(bs, p->Messaging_API_ID, 32);
	GF_IPMPX_WriteByteArray(bs, p->opaqueData);
	return GF_OK;
}

static GF_IPMPX_Data *NewGF_IPMPX_ISMACryp()
{
	GF_IPMPX_ISMACryp *ptr;
	GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ISMACryp, GF_IPMPX_ISMACRYP_TAG);
	return (GF_IPMPX_Data *)ptr;
}
static void DelGF_IPMPX_ISMACryp(GF_IPMPX_Data *_p)
{
	GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p;
	gf_free(p);
}
static GF_Err ReadGF_IPMPX_ISMACryp(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size)
{
	GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p;
	p->cryptoSuite = gf_bs_read_int(bs, 8);
	p->IV_length = gf_bs_read_int(bs, 8);
	p->use_selective_encryption = (Bool)gf_bs_read_int(bs, 1);
	gf_bs_read_int(bs, 7);
	p->key_indicator_length = gf_bs_read_int(bs, 8);
	return GF_OK;
}
static u32 SizeGF_IPMPX_ISMACryp(GF_IPMPX_Data *_p)
{
	return 4;
}
static GF_Err WriteGF_IPMPX_ISMACryp(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p;
	gf_bs_write_int(bs, p->cryptoSuite, 8);
	gf_bs_write_int(bs, p->IV_length, 8);
	gf_bs_write_int(bs, p->use_selective_encryption ? 1 : 0, 1);
	gf_bs_write_int(bs, 0, 7);
	gf_bs_write_int(bs, p->key_indicator_length, 8);
	return GF_OK;
}

GF_IPMPX_Data *gf_ipmpx_data_new(u8 tag)
{
	switch (tag) {
	case GF_IPMPX_RIGHTS_DATA_TAG:
	case GF_IPMPX_OPAQUE_DATA_TAG:
		return NewGF_IPMPX_OpaqueData(tag);
	case GF_IPMPX_KEY_DATA_TAG:
		return NewGF_IPMPX_KeyData();
	case GF_IPMPX_SECURE_CONTAINER_TAG:
		return NewGF_IPMPX_SecureContainer();
	case GF_IPMPX_ADD_TOOL_LISTENER_TAG:
		return NewGF_IPMPX_AddToolNotificationListener();
	case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG:
		return NewGF_IPMPX_RemoveToolNotificationListener();
	case GF_IPMPX_INIT_AUTHENTICATION_TAG:
		return NewGF_IPMPX_InitAuthentication();
	case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG:
		return NewGF_IPMPX_MutualAuthentication();
	case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG:
		return NewGF_IPMPX_ParametricDescription();
	case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG:
		return NewGF_IPMPX_ToolParamCapabilitiesQuery();
	case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG:
		return NewGF_IPMPX_ToolParamCapabilitiesResponse();
	case GF_IPMPX_GET_TOOLS_RESPONSE_TAG:
		return NewGF_IPMPX_GetToolsResponse();
	case GF_IPMPX_GET_TOOL_CONTEXT_TAG:
		return NewGF_IPMPX_GetToolContext();
	case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG:
		return NewGF_IPMPX_GetToolContextResponse();
	case GF_IPMPX_CONNECT_TOOL_TAG:
		return NewGF_IPMPX_ConnectTool();
	case GF_IPMPX_DISCONNECT_TOOL_TAG:
		return NewGF_IPMPX_DisconnectTool();
	case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG:
		return NewGF_IPMPX_NotifyToolEvent();
	case GF_IPMPX_CAN_PROCESS_TAG:
		return NewGF_IPMPX_CanProcess();
	case GF_IPMPX_TRUST_SECURITY_METADATA_TAG:
		return NewGF_IPMPX_TrustSecurityMetadata();
	case GF_IPMPX_ISMACRYP_TAG:
		return NewGF_IPMPX_ISMACryp();
	case GF_IPMPX_GET_TOOLS_TAG:
	{
		GF_IPMPX_Data *p;
		GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_Data, GF_IPMPX_GET_TOOLS_TAG);
		return p;
	}
	case GF_IPMPX_TRUSTED_TOOL_TAG:
	{
		GF_IPMPX_TrustedTool *p;
		GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_TrustedTool, GF_IPMPX_TRUSTED_TOOL_TAG);
		p->trustSpecifications = gf_list_new();
		return (GF_IPMPX_Data *)p;
	}
	case GF_IPMPX_TRUST_SPECIFICATION_TAG:
	{
		GF_IPMPX_TrustSpecification *p;
		GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_TrustSpecification, GF_IPMPX_TRUST_SPECIFICATION_TAG);
		return (GF_IPMPX_Data *)p;
	}
	case GF_IPMPX_TOOL_API_CONFIG_TAG:
		return NewGF_IPMPX_ToolAPI_Config();
	case GF_IPMPX_SEL_DEC_INIT_TAG:
		return NewGF_IPMPX_SelectiveDecryptionInit();
	case GF_IPMPX_AUDIO_WM_INIT_TAG:
	case GF_IPMPX_VIDEO_WM_INIT_TAG:
		return NewGF_IPMPX_WatermarkingInit(tag);
	case GF_IPMPX_AUDIO_WM_SEND_TAG:
	case GF_IPMPX_VIDEO_WM_SEND_TAG:
		return NewGF_IPMPX_SendWatermark(tag);

	case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG:
	{
		GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)gf_malloc(sizeof(GF_IPMPX_AUTH_AlgorithmDescriptor));
		if (!p) return NULL;
		memset(p, 0, sizeof(GF_IPMPX_AUTH_AlgorithmDescriptor));
		p->tag = GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG;
		return (GF_IPMPX_Data *)p;
	}
	case GF_IPMPX_KEY_DESCRIPTOR_TAG:
	{
		GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)gf_malloc(sizeof(GF_IPMPX_AUTH_KeyDescriptor));
		if (!p) return NULL;
		memset(p, 0, sizeof(GF_IPMPX_AUTH_KeyDescriptor));
		p->tag = GF_IPMPX_KEY_DESCRIPTOR_TAG;
		return (GF_IPMPX_Data *)p;
	}

	case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG:
	{
		GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)gf_malloc(sizeof(GF_IPMPX_ParametricDescriptionItem));
		if (!p) return NULL;
		memset(p, 0, sizeof(GF_IPMPX_ParametricDescriptionItem));
		p->tag = GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG;
		return (GF_IPMPX_Data *)p;
	}
	case GF_IPMPX_SEL_ENC_BUFFER_TAG:
	{
		GF_IPMPX_SelEncBuffer*p = (GF_IPMPX_SelEncBuffer*)gf_malloc(sizeof(GF_IPMPX_SelEncBuffer));
		if (!p) return NULL;
		memset(p, 0, sizeof(GF_IPMPX_SelEncBuffer));
		p->tag = GF_IPMPX_SEL_ENC_BUFFER_TAG;
		return (GF_IPMPX_Data *)p;
	}
	case GF_IPMPX_SEL_ENC_FIELD_TAG:
	{
		GF_IPMPX_SelEncField*p = (GF_IPMPX_SelEncField*)gf_malloc(sizeof(GF_IPMPX_SelEncField));
		if (!p) return NULL;
		memset(p, 0, sizeof(GF_IPMPX_SelEncField));
		p->tag = GF_IPMPX_SEL_ENC_FIELD_TAG;
		return (GF_IPMPX_Data *)p;
	}

	/*
	case GF_IPMPX_USER_QUERY_TAG: return NewGF_IPMPX_UserQuery();
	case GF_IPMPX_USER_RESPONSE_TAG: return NewGF_IPMPX_UserQueryResponse();
	*/
	default:
		return NULL;
	}

}

void gf_ipmpx_data_del(GF_IPMPX_Data *_p)
{
	if (!_p) return;
	switch (_p->tag) {
	case GF_IPMPX_RIGHTS_DATA_TAG:
	case GF_IPMPX_OPAQUE_DATA_TAG:
		DelGF_IPMPX_OpaqueData(_p);
		return;
	case GF_IPMPX_KEY_DATA_TAG:
		DelGF_IPMPX_KeyData(_p);
		return;
	case GF_IPMPX_SECURE_CONTAINER_TAG:
		DelGF_IPMPX_SecureContainer(_p);
		return;
	case GF_IPMPX_ADD_TOOL_LISTENER_TAG:
		DelGF_IPMPX_AddToolNotificationListener(_p);
		return;
	case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG:
		DelGF_IPMPX_RemoveToolNotificationListener(_p);
		return;
	case GF_IPMPX_INIT_AUTHENTICATION_TAG:
		DelGF_IPMPX_InitAuthentication(_p);
		return;
	case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG:
		DelGF_IPMPX_MutualAuthentication(_p);
		return;
	case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG:
		DelGF_IPMPX_ParametricDescription(_p);
		return;
	case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG:
		DelGF_IPMPX_ToolParamCapabilitiesQuery(_p);
		return;
	case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG:
		DelGF_IPMPX_ToolParamCapabilitiesResponse(_p);
		return;
	case GF_IPMPX_GET_TOOLS_RESPONSE_TAG:
		DelGF_IPMPX_GetToolsResponse(_p);
		return;
	case GF_IPMPX_GET_TOOL_CONTEXT_TAG:
		DelGF_IPMPX_GetToolContext(_p);
		return;
	case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG:
		DelGF_IPMPX_GetToolContextResponse(_p);
		return;
	case GF_IPMPX_CONNECT_TOOL_TAG:
		DelGF_IPMPX_ConnectTool(_p);
		return;
	case GF_IPMPX_DISCONNECT_TOOL_TAG:
		DelGF_IPMPX_DisconnectTool(_p);
		return;
	case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG:
		DelGF_IPMPX_NotifyToolEvent(_p);
		return;
	case GF_IPMPX_CAN_PROCESS_TAG:
		DelGF_IPMPX_CanProcess(_p);
		return;
	case GF_IPMPX_TRUST_SECURITY_METADATA_TAG:
		DelGF_IPMPX_TrustSecurityMetadata(_p);
		return;
	case GF_IPMPX_TOOL_API_CONFIG_TAG:
		DelGF_IPMPX_ToolAPI_Config(_p);
		return;
	case GF_IPMPX_ISMACRYP_TAG:
		DelGF_IPMPX_ISMACryp(_p);
		return;
	case GF_IPMPX_SEL_DEC_INIT_TAG:
		DelGF_IPMPX_SelectiveDecryptionInit(_p);
		return;
	case GF_IPMPX_AUDIO_WM_INIT_TAG:
	case GF_IPMPX_VIDEO_WM_INIT_TAG:
		DelGF_IPMPX_WatermarkingInit(_p);
		return;
	case GF_IPMPX_AUDIO_WM_SEND_TAG:
	case GF_IPMPX_VIDEO_WM_SEND_TAG:
		DelGF_IPMPX_SendWatermark(_p);
		return;

		/*
		case GF_IPMPX_USER_QUERY_TAG: DelGF_IPMPX_UserQuery(_p); return;
		case GF_IPMPX_USER_RESPONSE_TAG: DelGF_IPMPX_UserQueryResponse(_p); return;
		*/
	case GF_IPMPX_TRUSTED_TOOL_TAG:
	{
		GF_IPMPX_TrustedTool *p = (GF_IPMPX_TrustedTool *)_p;
		while (gf_list_count(p->trustSpecifications)) {
			GF_IPMPX_Data *ts = (GF_IPMPX_Data *)gf_list_get(p->trustSpecifications, 0);
			gf_list_rem(p->trustSpecifications, 0);
			gf_ipmpx_data_del(ts);
		}
		gf_list_del(p->trustSpecifications);
		gf_free(p);
		return;
	}
	case GF_IPMPX_TRUST_SPECIFICATION_TAG:
	{
		GF_IPMPX_TrustSpecification *p = (GF_IPMPX_TrustSpecification *)_p;
		GF_IPMPX_DELETE_ARRAY(p->CCTrustMetadata);
		gf_free(p);
		return;
	}
	case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG:
	{
		GF_IPMPX_ParametricDescriptionItem *p = (GF_IPMPX_ParametricDescriptionItem*)_p;
		GF_IPMPX_DELETE_ARRAY(p->main_class);
		GF_IPMPX_DELETE_ARRAY(p->subClass);
		GF_IPMPX_DELETE_ARRAY(p->typeData);
		GF_IPMPX_DELETE_ARRAY(p->type);
		GF_IPMPX_DELETE_ARRAY(p->addedData);
		gf_free(p);
		return;
	}
	case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG:
		_p->tag = GF_IPMPX_AUTH_AlgorithmDescr_Tag;
		GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)_p);
		return;
	case GF_IPMPX_KEY_DESCRIPTOR_TAG:
		_p->tag = GF_IPMPX_AUTH_KeyDescr_Tag;
		GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)_p);
		return;

	case GF_IPMPX_SEL_ENC_BUFFER_TAG:
	{
		GF_IPMPX_SelEncBuffer*p = (GF_IPMPX_SelEncBuffer*)_p;
		GF_IPMPX_DELETE_ARRAY(p->Stream_Cipher_Specific_Init_Info);
		gf_free(p);
		return;
	}
	case GF_IPMPX_SEL_ENC_FIELD_TAG:
	{
		GF_IPMPX_SelEncField*p = (GF_IPMPX_SelEncField*)_p;
		GF_IPMPX_DELETE_ARRAY(p->shuffleSpecificInfo);
		if (p->mappingTable) gf_free(p->mappingTable);
		gf_free(p);
		return;
	}

	case GF_IPMPX_GET_TOOLS_TAG:
	default:
		gf_free(_p);
		return;
	}
}

GF_Err GF_IPMPX_ReadData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 read)
{
	switch (_p->tag) {
	case GF_IPMPX_RIGHTS_DATA_TAG:
	case GF_IPMPX_OPAQUE_DATA_TAG:
		return ReadGF_IPMPX_OpaqueData(bs, _p, read);
	case GF_IPMPX_KEY_DATA_TAG:
		return ReadGF_IPMPX_KeyData(bs, _p, read);
	case GF_IPMPX_SECURE_CONTAINER_TAG:
		return ReadGF_IPMPX_SecureContainer(bs, _p, read);
	case GF_IPMPX_ADD_TOOL_LISTENER_TAG:
		return ReadGF_IPMPX_AddToolNotificationListener(bs, _p, read);
	case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG:
		return ReadGF_IPMPX_RemoveToolNotificationListener(bs, _p, read);
	case GF_IPMPX_INIT_AUTHENTICATION_TAG:
		return ReadGF_IPMPX_InitAuthentication(bs, _p, read);
	case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG:
		return ReadGF_IPMPX_MutualAuthentication(bs, _p, read);
	case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG:
		return ReadGF_IPMPX_ParametricDescription(bs, _p, read);
	case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG:
		return ReadGF_IPMPX_ToolParamCapabilitiesQuery(bs, _p, read);
	case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG:
		return ReadGF_IPMPX_ToolParamCapabilitiesResponse(bs, _p, read);
	case GF_IPMPX_GET_TOOLS_RESPONSE_TAG:
		return ReadGF_IPMPX_GetToolsResponse(bs, _p, read);
	case GF_IPMPX_GET_TOOL_CONTEXT_TAG:
		return ReadGF_IPMPX_GetToolContext(bs, _p, read);
	case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG:
		return ReadGF_IPMPX_GetToolContextResponse(bs, _p, read);
	case GF_IPMPX_CONNECT_TOOL_TAG:
		return ReadGF_IPMPX_ConnectTool(bs, _p, read);
	case GF_IPMPX_DISCONNECT_TOOL_TAG:
		return ReadGF_IPMPX_DisconnectTool(bs, _p, read);
	case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG:
		return ReadGF_IPMPX_NotifyToolEvent(bs, _p, read);
	case GF_IPMPX_CAN_PROCESS_TAG:
		return ReadGF_IPMPX_CanProcess(bs, _p, read);
	case GF_IPMPX_TRUST_SECURITY_METADATA_TAG:
		return ReadGF_IPMPX_TrustSecurityMetadata(bs, _p, read);
	case GF_IPMPX_TOOL_API_CONFIG_TAG:
		return ReadGF_IPMPX_ToolAPI_Config(bs, _p, read);
	case GF_IPMPX_ISMACRYP_TAG:
		return ReadGF_IPMPX_ISMACryp(bs, _p, read);
	case GF_IPMPX_SEL_DEC_INIT_TAG:
		return ReadGF_IPMPX_SelectiveDecryptionInit(bs, _p, read);
	case GF_IPMPX_AUDIO_WM_INIT_TAG:
	case GF_IPMPX_VIDEO_WM_INIT_TAG:
		return ReadGF_IPMPX_WatermarkingInit(bs, _p, read);
	case GF_IPMPX_AUDIO_WM_SEND_TAG:
	case GF_IPMPX_VIDEO_WM_SEND_TAG:
		return ReadGF_IPMPX_SendWatermark(bs, _p, read);

		/*
		case GF_IPMPX_USER_QUERY_TAG: return ReadGF_IPMPX_UserQuery(bs, _p, read);
		case GF_IPMPX_USER_RESPONSE_TAG: return ReadGF_IPMPX_UserQueryResponse(bs, _p, read);
		*/
	case GF_IPMPX_GET_TOOLS_TAG:
		return GF_OK;
	default:
		return GF_BAD_PARAM;
	}
}

u32 gf_ipmpx_data_size(GF_IPMPX_Data *_p)
{
	switch (_p->tag) {
	case GF_IPMPX_RIGHTS_DATA_TAG:
	case GF_IPMPX_OPAQUE_DATA_TAG:
		return SizeGF_IPMPX_OpaqueData(_p);
	case GF_IPMPX_KEY_DATA_TAG:
		return SizeGF_IPMPX_KeyData(_p);
	case GF_IPMPX_SECURE_CONTAINER_TAG:
		return SizeGF_IPMPX_SecureContainer(_p);
	case GF_IPMPX_ADD_TOOL_LISTENER_TAG:
		return SizeGF_IPMPX_AddToolNotificationListener(_p);
	case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG:
		return SizeGF_IPMPX_RemoveToolNotificationListener(_p);
	case GF_IPMPX_INIT_AUTHENTICATION_TAG:
		return SizeGF_IPMPX_InitAuthentication(_p);
	case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG:
		return SizeGF_IPMPX_MutualAuthentication(_p);
	case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG:
		return SizeGF_IPMPX_ParametricDescription(_p);
	case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG:
		return SizeGF_IPMPX_ToolParamCapabilitiesQuery(_p);
	case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG:
		return SizeGF_IPMPX_ToolParamCapabilitiesResponse(_p);
	case GF_IPMPX_GET_TOOLS_RESPONSE_TAG:
		return SizeGF_IPMPX_GetToolsResponse(_p);
	case GF_IPMPX_GET_TOOL_CONTEXT_TAG:
		return SizeGF_IPMPX_GetToolContext(_p);
	case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG:
		return SizeGF_IPMPX_GetToolContextResponse(_p);
	case GF_IPMPX_CONNECT_TOOL_TAG:
		return SizeGF_IPMPX_ConnectTool(_p);
	case GF_IPMPX_DISCONNECT_TOOL_TAG:
		return SizeGF_IPMPX_DisconnectTool(_p);
	case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG:
		return SizeGF_IPMPX_NotifyToolEvent(_p);
	case GF_IPMPX_CAN_PROCESS_TAG:
		return SizeGF_IPMPX_CanProcess(_p);
	case GF_IPMPX_TRUST_SECURITY_METADATA_TAG:
		return SizeGF_IPMPX_TrustSecurityMetadata(_p);
	case GF_IPMPX_TOOL_API_CONFIG_TAG:
		return SizeGF_IPMPX_ToolAPI_Config(_p);
	case GF_IPMPX_ISMACRYP_TAG:
		return SizeGF_IPMPX_ISMACryp(_p);
	case GF_IPMPX_SEL_DEC_INIT_TAG:
		return SizeGF_IPMPX_SelectiveDecryptionInit(_p);
	case GF_IPMPX_AUDIO_WM_INIT_TAG:
	case GF_IPMPX_VIDEO_WM_INIT_TAG:
		return SizeGF_IPMPX_WatermarkingInit(_p);
	case GF_IPMPX_AUDIO_WM_SEND_TAG:
	case GF_IPMPX_VIDEO_WM_SEND_TAG:
		return SizeGF_IPMPX_SendWatermark(_p);

		/*
		case GF_IPMPX_USER_QUERY_TAG: return SizeGF_IPMPX_UserQuery(_p);
		case GF_IPMPX_USER_RESPONSE_TAG: return SizeGF_IPMPX_UserQueryResponse(_p);
		*/
	case GF_IPMPX_GET_TOOLS_TAG:
		return 0;
	default:
		return GF_BAD_PARAM;
	}
}

GF_Err gf_ipmpx_data_write(GF_BitStream *bs, GF_IPMPX_Data *_p)
{
	GF_Err e;
	if (!_p) return GF_OK;
	e = GF_IPMPX_WriteBase(bs, _p);
	if (e) return e;
	switch (_p->tag) {
	case GF_IPMPX_RIGHTS_DATA_TAG:
	case GF_IPMPX_OPAQUE_DATA_TAG:
		return WriteGF_IPMPX_OpaqueData(bs, _p);
	case GF_IPMPX_KEY_DATA_TAG:
		return WriteGF_IPMPX_KeyData(bs, _p);
	case GF_IPMPX_SECURE_CONTAINER_TAG:
		return WriteGF_IPMPX_SecureContainer(bs, _p);
	case GF_IPMPX_ADD_TOOL_LISTENER_TAG:
		return WriteGF_IPMPX_AddToolNotificationListener(bs, _p);
	case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG:
		return WriteGF_IPMPX_RemoveToolNotificationListener(bs, _p);
	case GF_IPMPX_INIT_AUTHENTICATION_TAG:
		return WriteGF_IPMPX_InitAuthentication(bs, _p);
	case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG:
		return WriteGF_IPMPX_MutualAuthentication(bs, _p);
	case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG:
		return WriteGF_IPMPX_ParametricDescription(bs, _p);
	case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG:
		return WriteGF_IPMPX_ToolParamCapabilitiesQuery(bs, _p);
	case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG:
		return WriteGF_IPMPX_ToolParamCapabilitiesResponse(bs, _p);
	case GF_IPMPX_GET_TOOLS_RESPONSE_TAG:
		return WriteGF_IPMPX_GetToolsResponse(bs, _p);
	case GF_IPMPX_GET_TOOL_CONTEXT_TAG:
		return WriteGF_IPMPX_GetToolContext(bs, _p);
	case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG:
		return WriteGF_IPMPX_GetToolContextResponse(bs, _p);
	case GF_IPMPX_CONNECT_TOOL_TAG:
		return WriteGF_IPMPX_ConnectTool(bs, _p);
	case GF_IPMPX_DISCONNECT_TOOL_TAG:
		return WriteGF_IPMPX_DisconnectTool(bs, _p);
	case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG:
		return WriteGF_IPMPX_NotifyToolEvent(bs, _p);
	case GF_IPMPX_CAN_PROCESS_TAG:
		return WriteGF_IPMPX_CanProcess(bs, _p);
	case GF_IPMPX_TRUST_SECURITY_METADATA_TAG:
		return WriteGF_IPMPX_TrustSecurityMetadata(bs, _p);
	case GF_IPMPX_TOOL_API_CONFIG_TAG:
		return WriteGF_IPMPX_ToolAPI_Config(bs, _p);
	case GF_IPMPX_ISMACRYP_TAG:
		return WriteGF_IPMPX_ISMACryp(bs, _p);
	case GF_IPMPX_SEL_DEC_INIT_TAG:
		return WriteGF_IPMPX_SelectiveDecryptionInit(bs, _p);
	case GF_IPMPX_AUDIO_WM_INIT_TAG:
	case GF_IPMPX_VIDEO_WM_INIT_TAG:
		return WriteGF_IPMPX_WatermarkingInit(bs, _p);
	case GF_IPMPX_AUDIO_WM_SEND_TAG:
	case GF_IPMPX_VIDEO_WM_SEND_TAG:
		return WriteGF_IPMPX_SendWatermark(bs, _p);

		/*
		case GF_IPMPX_USER_QUERY_TAG: return WriteGF_IPMPX_UserQuery(bs, _p);
		case GF_IPMPX_USER_RESPONSE_TAG: return WriteGF_IPMPX_UserQueryResponse(bs, _p);
		*/
	case GF_IPMPX_GET_TOOLS_TAG:
		return GF_OK;
	default:
		return GF_BAD_PARAM;
	}
}

#endif /*GPAC_MINIMAL_ODF*/
