/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005 
 *					All rights reserved
 *
 *  This file is part of GPAC / Object Descriptor sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU 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 General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include <gpac/intern/m4_od_dev.h>

#define IPMPX_DATA_ALLOC(__ptr, __size, __tag) \
	__ptr = malloc(__size); \
	if (!__ptr) return NULL;	\
	memset(__ptr, 0, __size);	\
	((IPMPX_Data *)__ptr)->tag = __tag;	\
	((IPMPX_Data *)__ptr)->Version = 0x01;	\


#define IPMPX_DELETE_ARRAY(__ar) if (__ar) { if (__ar->data) free(__ar->data); free(__ar); }

u32 M4INLINE 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(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) {
		BS_WriteInt(bs, vals[3], 8);
	} else if (size < 0x00004000) {
		BS_WriteInt(bs, vals[2], 8);
		BS_WriteInt(bs, vals[3], 8);
	} else if (size < 0x00200000) {
		BS_WriteInt(bs, vals[1], 8);
		BS_WriteInt(bs, vals[2], 8);
		BS_WriteInt(bs, vals[3], 8);
	} else if (size < 0x10000000) {
		BS_WriteInt(bs, vals[0], 8);
		BS_WriteInt(bs, vals[1], 8);
		BS_WriteInt(bs, vals[2], 8);
		BS_WriteInt(bs, vals[3], 8);
	} 
}

IPMPX_ByteArray *IPMPX_GetByteArray(BitStream *bs)
{
	IPMPX_ByteArray *ba;
	u32 val, size;
	size = 0;
	do {
		val = BS_ReadInt(bs, 8);
		size <<= 7;
		size |= val & 0x7F;
	} while ( val & 0x80 );
	if (!size) return NULL;
	ba = malloc(sizeof(IPMPX_ByteArray));
	ba->data = malloc(sizeof(char)*size);
	BS_ReadData(bs, ba->data, size);
	ba->length = size;
	return ba;
}

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

void IPMPX_WriteByteArray(BitStream *bs, IPMPX_ByteArray *ba)
{
	if (!ba || !ba->data) {
		write_var_size(bs, 0);
	} else {
		write_var_size(bs, ba->length);
		BS_WriteData(bs, ba->data, ba->length);
	}
}

void IPMPX_AUTH_Delete(IPMPX_Authentication *auth)
{
	if (!auth) return;
	switch (auth->tag) {
	case IPMPX_AUTH_AlgorithmDescr_Tag:
		{
			IPMPX_AUTH_AlgorithmDescriptor *p = (IPMPX_AUTH_AlgorithmDescriptor *)auth;
			IPMPX_DELETE_ARRAY(p->specAlgoID);
			IPMPX_DELETE_ARRAY(p->OpaqueData);
			free(p);
		}
		break;
	case IPMPX_AUTH_KeyDescr_Tag:
		{
			IPMPX_AUTH_KeyDescriptor *p = (IPMPX_AUTH_KeyDescriptor *)auth;
			if (p->keyBody) free(p->keyBody);
			free(p);
		}
		break;
	}
}

u32 IPMPX_AUTH_Size(IPMPX_Authentication *auth)
{
	u32 size;
	if (!auth) return 0;
	switch (auth->tag) {
	case IPMPX_AUTH_AlgorithmDescr_Tag:
		{
			IPMPX_AUTH_AlgorithmDescriptor *p = (IPMPX_AUTH_AlgorithmDescriptor *)auth;
			size = 1 + (p->specAlgoID ? IPMPX_GetByteArraySize(p->specAlgoID) : 2);
			size += IPMPX_GetByteArraySize(p->OpaqueData);
			return size;
		}
		break;
	case IPMPX_AUTH_KeyDescr_Tag:
		{
			IPMPX_AUTH_KeyDescriptor *p = (IPMPX_AUTH_KeyDescriptor *)auth;
			size = p->keyBodyLength;
			return size;
		}
		break;
	default:
		return 0;
	}
}

u32 IPMPX_AUTH_FullSize(IPMPX_Authentication *auth)
{
	u32 size = IPMPX_AUTH_Size(auth);
	size += get_field_size(size);
	size += 1;/*tag*/
	return size;
}

M4Err WriteIPMPX_AUTH(BitStream *bs, IPMPX_Authentication *auth)
{
	u32 size;

	if (!auth) return M4OK;
	BS_WriteInt(bs, auth->tag, 8);

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

	switch (auth->tag) {
	case IPMPX_AUTH_AlgorithmDescr_Tag:
		{
			IPMPX_AUTH_AlgorithmDescriptor *p = (IPMPX_AUTH_AlgorithmDescriptor *)auth;
			BS_WriteInt(bs, p->specAlgoID ? 0 : 1, 1);
			BS_WriteInt(bs, 0, 7);
			if (p->specAlgoID) {
				IPMPX_WriteByteArray(bs, p->specAlgoID);
			} else {
				BS_WriteInt(bs, p->regAlgoID, 16);
			}
			IPMPX_WriteByteArray(bs, p->OpaqueData);
		}
		break;
	case IPMPX_AUTH_KeyDescr_Tag:
		{
			IPMPX_AUTH_KeyDescriptor *p = (IPMPX_AUTH_KeyDescriptor *)auth;
			/*tag*/
			BS_WriteData(bs, p->keyBody, p->keyBodyLength);
		}
		break;
	default:
		break;
	}
	return M4OK;
}

M4Err IPMPX_AUTH_Parse(BitStream *bs, IPMPX_Authentication **auth)
{
	u32 val, size, tag;

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

	switch (tag) {
	case IPMPX_AUTH_KeyDescr_Tag:
		{
			IPMPX_AUTH_KeyDescriptor *p;
			SAFEALLOC(p, sizeof(IPMPX_AUTH_KeyDescriptor));
			if (!p) return M4OutOfMem;
			p->tag = tag;
			p->keyBodyLength = size;
			p->keyBody = malloc(sizeof(char)*size);
			BS_ReadData(bs, p->keyBody, size);
			*auth = (IPMPX_Authentication *)p;
			return M4OK;
		}
		break;
	case IPMPX_AUTH_AlgorithmDescr_Tag:
		{
			Bool isReg;
			IPMPX_AUTH_AlgorithmDescriptor *p;
			SAFEALLOC(p, sizeof(IPMPX_AUTH_AlgorithmDescriptor));
			if (!p) return M4OutOfMem;
			p->tag = tag;
			isReg = BS_ReadInt(bs, 1);
			BS_ReadInt(bs, 7);
			if (isReg) {
				p->regAlgoID = BS_ReadInt(bs, 16);
			} else {
				p->specAlgoID = IPMPX_GetByteArray(bs);
			}
			p->OpaqueData = IPMPX_GetByteArray(bs);
			*auth = (IPMPX_Authentication *)p;
			return M4OK;
		}
		break;
	default:
		break;
	}
	return M4NonCompliantBitStream;
}


M4Err IPMPX_ReadData(BitStream *bs, IPMPX_Data *_p, u32 size);

u32 IPMPX_GetDataFullSize(IPMPX_Data *p)
{
	u32 size;
	if (!p) return 0;
	size = IPMPX_GetDataSize(p);
	size += 5;	/*Version and dataID*/
	size += get_field_size(size);
	size += 1;/*tag*/
	return size;
}

M4Err IPMPX_ParseData(BitStream *bs, IPMPX_Data **out_data)
{
	M4Err e;
	u32 val, size;
	u8 tag;
	IPMPX_Data *p;

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

	p = IPMPX_NewData(tag);
	if (!p) return M4NonCompliantBitStream;
	p->Version = BS_ReadInt(bs, 8);
	p->dataID = BS_ReadInt(bs, 32);
	e = IPMPX_ReadData(bs, p, size);
	if (e) {
		IPMPX_DeleteData(p);
		return e;
	}
	*out_data = p;
	return M4OK;
}

M4Err IPMPX_WriteBase(BitStream *bs, IPMPX_Data *p)
{
	u32 size;

	if (!p) return M4BadParam;
	size = IPMPX_GetDataSize(p);
	size += 5;	/*Version & dataID*/
	BS_WriteInt(bs, p->tag, 8);
	write_var_size(bs, size);
	BS_WriteInt(bs, p->Version, 8);
	BS_WriteInt(bs, p->dataID, 32);
	return M4OK;
}


static IPMPX_Data *NewIPMPX_InitAuthentication()
{
	IPMPX_InitAuthentication *ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_InitAuthentication), IPMPX_InitAuthentication_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_InitAuthentication(IPMPX_Data *_p)
{
	IPMPX_InitAuthentication *p = (IPMPX_InitAuthentication*)_p;
	free(p);
}
static M4Err ReadIPMPX_InitAuthentication(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	IPMPX_InitAuthentication *p = (IPMPX_InitAuthentication*)_p;
	p->Context = BS_ReadInt(bs, 32);
	p->AuthType = BS_ReadInt(bs, 8);
	return M4OK;
}
static u32 SizeIPMPX_InitAuthentication(IPMPX_Data *_p)
{
	return 5;
}
static M4Err WriteIPMPX_InitAuthentication(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_InitAuthentication *p = (IPMPX_InitAuthentication*)_p;
	BS_WriteInt(bs, p->Context, 32);
	BS_WriteInt(bs, p->AuthType, 8);
	return M4OK;
}


static IPMPX_Data *NewIPMPX_MutualAuthentication()
{
	IPMPX_MutualAuthentication *ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_MutualAuthentication), IPMPX_MutualAuthentication_Tag);
	ptr->candidateAlgorithms = NewChain();
	ptr->agreedAlgorithms = NewChain();
	ptr->certificates = NewChain();
	return (IPMPX_Data *) ptr;
}

static void delete_algo_list(Chain *algos)
{
	u32 i;
	for (i=0;i<ChainGetCount(algos); i++) {
		IPMPX_Authentication *ip_auth = ChainGetEntry(algos, i);
		IPMPX_AUTH_Delete(ip_auth);
	}
	DeleteChain(algos);
}
static void DelIPMPX_MutualAuthentication(IPMPX_Data *_p)
{
	IPMPX_MutualAuthentication *p = (IPMPX_MutualAuthentication *)_p;
	delete_algo_list(p->candidateAlgorithms);
	delete_algo_list(p->agreedAlgorithms);
	IPMPX_DELETE_ARRAY(p->AuthenticationData);
	IPMPX_DELETE_ARRAY(p->opaque);
	IPMPX_DELETE_ARRAY(p->authCodes);
	IPMPX_DeleteData((IPMPX_Data *)p->trustData);
	IPMPX_AUTH_Delete((IPMPX_Authentication*)p->publicKey);
	while (ChainGetCount(p->certificates)) {
		IPMPX_ByteArray *ba = ChainGetEntry(p->certificates, 0);
		ChainDeleteEntry(p->certificates, 0);
		IPMPX_DELETE_ARRAY(ba);
	}
	DeleteChain(p->certificates);
	free(p);
}
static M4Err ReadIPMPX_MutualAuthentication(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	M4Err e;
	u32 i, count;
	Bool requestNegotiation, successNegotiation, inclAuthenticationData, inclAuthCodes;
	IPMPX_MutualAuthentication *p = (IPMPX_MutualAuthentication *)_p;
	
	requestNegotiation = BS_ReadInt(bs, 1);
	successNegotiation = BS_ReadInt(bs, 1);
	p->failedNegotiation =  BS_ReadInt(bs, 1);
	inclAuthenticationData = BS_ReadInt(bs, 1);
	inclAuthCodes = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 3);

	if (requestNegotiation) {
		count = BS_ReadInt(bs, 8);
		for (i=0; i<count; i++) {
			IPMPX_Authentication *auth;
			e = IPMPX_AUTH_Parse(bs, &auth);
			if (e) return e;
			ChainAddEntry(p->candidateAlgorithms, auth);
		}
	}
	if (successNegotiation) {
		count = BS_ReadInt(bs, 8);
		for (i=0; i<count; i++) {
			IPMPX_Authentication *auth;
			e = IPMPX_AUTH_Parse(bs, &auth);
			if (e) return e;
			ChainAddEntry(p->agreedAlgorithms, auth);
		}
	}
	if (inclAuthenticationData) p->AuthenticationData = IPMPX_GetByteArray(bs);
	if (inclAuthCodes) {
		/*unspecified in spec, IM1 uses 8 bits*/
		u32 type = BS_ReadInt(bs, 8);
		switch (type) {
		case 0x01:
			count = BS_ReadInt(bs, 8);
			p->certType = BS_ReadInt(bs, 32);
			for (i=0; i<count; i++) {
				IPMPX_ByteArray *ipd = IPMPX_GetByteArray(bs);
				if (ipd) ChainAddEntry(p->certificates, ipd);
			}
			break;
		case 0x02:
			e = IPMPX_AUTH_Parse(bs, (IPMPX_Authentication**) &p->publicKey);
			if (e) return e;
			break;
		case 0xFE:
			p->opaque = IPMPX_GetByteArray(bs);
			break;
		default:
			break;
		}
		e = IPMPX_ParseData(bs, (IPMPX_Data **) &p->trustData);
		if (e) return e;
		p->authCodes = IPMPX_GetByteArray(bs);
	}
	return M4OK;
}
static u32 SizeIPMPX_MutualAuthentication(IPMPX_Data *_p)
{
	u32 size, i, count;
	IPMPX_MutualAuthentication *p = (IPMPX_MutualAuthentication *)_p;
	size = 1;
	count = ChainGetCount(p->candidateAlgorithms);
	if (count) {
		size += 1;
		for (i=0; i<count; i++) {
			IPMPX_Authentication *ip_auth = ChainGetEntry(p->candidateAlgorithms, i);
			size += IPMPX_AUTH_FullSize(ip_auth);
		}
	}
	count = ChainGetCount(p->agreedAlgorithms);
	if (count) {
		size += 1;
		for (i=0; i<count; i++) {
			IPMPX_Authentication *ip_auth = ChainGetEntry(p->agreedAlgorithms, i);
			size += IPMPX_AUTH_FullSize(ip_auth);
		}
	}
	if (p->AuthenticationData) size += IPMPX_GetByteArraySize(p->AuthenticationData);
	
	count = ChainGetCount(p->certificates);
	if (count || p->opaque || p->publicKey) {
		size += 1;
		/*type 1*/
		if (count) {
			size += 1+4;
			for (i=0; i<count; i++) {
				IPMPX_ByteArray *ba = ChainGetEntry(p->certificates, i);
				size += IPMPX_GetByteArraySize(ba);
			}
		}
		/*type 2*/
		else if (p->publicKey) {
			size += IPMPX_AUTH_FullSize((IPMPX_Authentication*)p->publicKey);
		}
		/*type 0xFE*/
		else if (p->opaque) {
			size += IPMPX_GetByteArraySize(p->opaque);
		}
		size += IPMPX_GetDataFullSize((IPMPX_Data *)p->trustData);
		size += IPMPX_GetByteArraySize(p->authCodes);
	}
	return size;
}
static M4Err WriteIPMPX_MutualAuthentication(BitStream *bs, IPMPX_Data *_p)
{
	u32 i, count;
	IPMPX_MutualAuthentication *p = (IPMPX_MutualAuthentication *)_p;

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

	count = ChainGetCount(p->candidateAlgorithms);
	if (count) {
		BS_WriteInt(bs, count, 8);
		for (i=0; i<count; i++) {
			IPMPX_Authentication *ip_auth = ChainGetEntry(p->candidateAlgorithms, i);
			WriteIPMPX_AUTH(bs, ip_auth);
		}
	}
	count = ChainGetCount(p->agreedAlgorithms);
	if (count) {
		BS_WriteInt(bs, count, 8);
		for (i=0; i<count; i++) {
			IPMPX_Authentication *ip_auth = ChainGetEntry(p->agreedAlgorithms, i);
			WriteIPMPX_AUTH(bs, ip_auth);
		}
	}
	if (p->AuthenticationData) IPMPX_WriteByteArray(bs, p->AuthenticationData);
	
	count = ChainGetCount(p->certificates);
	if (count || p->opaque || p->publicKey) {
		/*type 1*/
		if (count) {
			BS_WriteInt(bs, 0x01, 8);

			BS_WriteInt(bs, count, 8);
			BS_WriteInt(bs, p->certType, 32);
			for (i=0; i<count; i++) {
				IPMPX_ByteArray *ipd = ChainGetEntry(p->certificates, i);
				if (ipd) IPMPX_WriteByteArray(bs, ipd);
			}
		}
		/*type 2*/
		else if (p->publicKey) {
			BS_WriteInt(bs, 0x02, 8);
			WriteIPMPX_AUTH(bs, (IPMPX_Authentication *) p->publicKey);
		}
		/*type 0xFE*/
		else if (p->opaque) {
			BS_WriteInt(bs, 0xFE, 8);
			IPMPX_WriteByteArray(bs, p->opaque);
		}
		IPMPX_WriteData(bs, (IPMPX_Data *)p->trustData);
		IPMPX_WriteByteArray(bs, p->authCodes);
	}
	return M4OK;
}

static IPMPX_Data *NewIPMPX_TrustSecurityMetadata()
{
	IPMPX_TrustSecurityMetadata *ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_TrustSecurityMetadata), IPMPX_TrustSecurityMetadata_Tag);
	ptr->TrustedTools = NewChain();
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_TrustSecurityMetadata(IPMPX_Data *_p)
{
	IPMPX_TrustSecurityMetadata *p = (IPMPX_TrustSecurityMetadata *)_p;
	while (ChainGetCount(p->TrustedTools)) {
		IPMPX_TrustedTool *tt = ChainGetEntry(p->TrustedTools, 0);
		ChainDeleteEntry(p->TrustedTools, 0);
		while (ChainGetCount(tt->trustSpecifications)) {
			IPMPX_TrustSpecification *tts = ChainGetEntry(tt->trustSpecifications, 0);
			ChainDeleteEntry(tt->trustSpecifications, 0);
			IPMPX_DELETE_ARRAY(tts->CCTrustMetadata);
			free(tts);
		}
		DeleteChain(tt->trustSpecifications);
		free(tt);
	}
	DeleteChain(p->TrustedTools);
	free(p);
}
static M4Err ReadIPMPX_TrustSecurityMetadata(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	u32 nbTools, nbSpec;
	IPMPX_TrustSecurityMetadata *p = (IPMPX_TrustSecurityMetadata *)_p;
	nbTools = BS_ReadInt(bs, 16);
	while (nbTools) {
		IPMPX_TrustedTool *tt = malloc(sizeof(IPMPX_TrustedTool));
		if (!tt) return M4OutOfMem;
		memset(tt, 0, sizeof(IPMPX_TrustedTool));
		tt->tag = IPMPX_TrustedTool_Tag;
		nbTools--;
		ChainAddEntry(p->TrustedTools, tt);
		BS_ReadData(bs, (unsigned char *)tt->toolID, 16);
		BS_ReadData(bs, (unsigned char *)tt->AuditDate, 5);
		tt->trustSpecifications = NewChain();
		nbSpec = BS_ReadInt(bs, 16);
		while (nbSpec) {
			Bool has_cc;
			IPMPX_TrustSpecification *tts = malloc(sizeof(IPMPX_TrustSpecification));
			if (!tts) return M4OutOfMem;
			memset(tts, 0, sizeof(IPMPX_TrustSpecification));
			tts->tag = IPMPX_TrustSpecification_Tag;
			nbSpec--;
			ChainAddEntry(tt->trustSpecifications, tts);
			has_cc = BS_ReadInt(bs, 1);
			BS_ReadInt(bs, 7);
			if (has_cc) {
				tts->CCTrustMetadata = IPMPX_GetByteArray(bs);
			} else {
				BS_ReadData(bs, (unsigned char *)tts->startDate, 5);
				tts->attackerProfile = BS_ReadInt(bs, 2);
				BS_ReadInt(bs, 6);
				tts->trustedDuration = BS_ReadInt(bs, 32);
			}
		}
	}
	return M4OK;
}
static u32 SizeIPMPX_TrustSecurityMetadata(IPMPX_Data *_p)
{
	u32 size, i, j;
	IPMPX_TrustSecurityMetadata *p = (IPMPX_TrustSecurityMetadata *)_p;
	size = 2;
	for (i=0;i<ChainGetCount(p->TrustedTools); i++) {
		IPMPX_TrustedTool *tt = ChainGetEntry(p->TrustedTools, i);
		size += 23;
		for (j=0; j<ChainGetCount(tt->trustSpecifications); j++) {
			IPMPX_TrustSpecification *tts = ChainGetEntry(tt->trustSpecifications, j);
			size += 1;
			if (tts->CCTrustMetadata) size += IPMPX_GetByteArraySize(tts->CCTrustMetadata);
			else size += 10;
		}
	}
	return size;
}
static M4Err WriteIPMPX_TrustSecurityMetadata(BitStream *bs, IPMPX_Data *_p)
{
	u32 i, j, c1, c2;
	IPMPX_TrustSecurityMetadata *p = (IPMPX_TrustSecurityMetadata *)_p;

	c1 = ChainGetCount(p->TrustedTools);
	BS_WriteInt(bs, c1, 16);
	for (i=0;i<c1; i++) {
		IPMPX_TrustedTool *tt = ChainGetEntry(p->TrustedTools, i);
		BS_WriteData(bs, (unsigned char *)tt->toolID, 16);
		BS_WriteData(bs, (unsigned char *)tt->AuditDate, 5);
		c2 = ChainGetCount(tt->trustSpecifications);
		BS_WriteInt(bs, c2, 16);

		for (j=0; j<c2; j++) {
			IPMPX_TrustSpecification *tts = ChainGetEntry(tt->trustSpecifications, j);
			BS_WriteInt(bs, tts->CCTrustMetadata ? 1 : 0, 1);
			BS_WriteInt(bs, 0, 7);
			if (tts->CCTrustMetadata) IPMPX_WriteByteArray(bs, tts->CCTrustMetadata);
			else {
				BS_WriteData(bs, (unsigned char *)tts->startDate, 5);
				BS_WriteInt(bs, tts->attackerProfile, 2);
				BS_WriteInt(bs, 0, 6);
				BS_WriteInt(bs, tts->trustedDuration, 32);
			}
		}
	}
	return M4OK;
}

static IPMPX_Data *NewIPMPX_SecureContainer()
{
	IPMPX_SecureContainer *ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_SecureContainer), IPMPX_SecureContainer_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_SecureContainer(IPMPX_Data *_p)
{
	IPMPX_SecureContainer *p = (IPMPX_SecureContainer *)_p;
	IPMPX_DELETE_ARRAY(p->encryptedData);
	IPMPX_DELETE_ARRAY(p->MAC);
	IPMPX_DeleteData(p->protectedMsg);
	free(p);
}
static M4Err ReadIPMPX_SecureContainer(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	Bool has_enc, has_mac;
	IPMPX_SecureContainer *p = (IPMPX_SecureContainer *)_p;
	has_enc = BS_ReadInt(bs, 1);
	has_mac = BS_ReadInt(bs, 1);
	p->isMACEncrypted = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 5);
	if (has_enc) {
		p->encryptedData = IPMPX_GetByteArray(bs);
		if (has_mac && !p->isMACEncrypted) p->MAC = IPMPX_GetByteArray(bs);
	} else {
		M4Err e = IPMPX_ParseData(bs, &p->protectedMsg);
		if (e) return e;
		if (has_mac) p->MAC = IPMPX_GetByteArray(bs);
	}
	return M4OK;
}
static u32 SizeIPMPX_SecureContainer(IPMPX_Data *_p)
{
	u32 size = 1;
	IPMPX_SecureContainer *p = (IPMPX_SecureContainer *)_p;
	if (p->MAC) p->isMACEncrypted = 0;
	if (p->encryptedData) {
		size += IPMPX_GetByteArraySize(p->encryptedData);
		if (p->MAC) size += IPMPX_GetByteArraySize(p->MAC);
	} else {
		size += IPMPX_GetDataFullSize(p->protectedMsg);
		if (p->MAC) size += IPMPX_GetByteArraySize(p->MAC);
	}
	return size;
}
static M4Err WriteIPMPX_SecureContainer(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_SecureContainer *p = (IPMPX_SecureContainer *)_p;
	if (p->MAC) p->isMACEncrypted = 0;
	BS_WriteInt(bs, p->encryptedData ? 1 : 0, 1);
	BS_WriteInt(bs, (p->MAC || p->isMACEncrypted) ? 1 : 0, 1);
	BS_WriteInt(bs, p->isMACEncrypted, 1);
	BS_WriteInt(bs, 0, 5);
	if (p->encryptedData) {
		IPMPX_WriteByteArray(bs, p->encryptedData);
		if (p->MAC) IPMPX_WriteByteArray(bs, p->MAC);
	} else {
		M4Err e = IPMPX_WriteData(bs, p->protectedMsg);
		if (e) return e;
		if (p->MAC) IPMPX_WriteByteArray(bs, p->MAC);
	}
	return M4OK;
}

static IPMPX_Data *NewIPMPX_GetToolsResponse()
{
	IPMPX_GetToolsResponse *ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_GetToolsResponse), IPMPX_GetToolsResponse_Tag);
	ptr->ipmp_tools = NewChain();
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_GetToolsResponse(IPMPX_Data *_p)
{
	IPMPX_GetToolsResponse *p = (IPMPX_GetToolsResponse *)_p;
	while (ChainGetCount(p->ipmp_tools)) {
		/*IPMPTools are descriptors*/
		Descriptor *d = ChainGetEntry(p->ipmp_tools, 0);
		ChainDeleteEntry(p->ipmp_tools, 0);
		OD_DeleteDescriptor((Descriptor**)&d);
	}
	DeleteChain(p->ipmp_tools);
	free(p);
}
static M4Err ReadIPMPX_GetToolsResponse(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	u32 NbBytes = 0;
	IPMPX_GetToolsResponse *p = (IPMPX_GetToolsResponse *)_p;

	while (size>NbBytes) {
		u32 desc_size, start_o;
		Descriptor *desc;
		M4Err e;
		start_o = (u32) BS_GetPosition(bs);
		e = ParseDescriptor(bs, &desc, &desc_size);
		if (e) return e;
		ChainAddEntry(p->ipmp_tools, desc);
		NbBytes += (u32) BS_GetPosition(bs) - start_o;
	}
	if (size<NbBytes) return M4NonCompliantBitStream;
	return M4OK;
}
static u32 SizeIPMPX_GetToolsResponse(IPMPX_Data *_p)
{
	u32 size = 0;
	IPMPX_GetToolsResponse *p = (IPMPX_GetToolsResponse *)_p;
	calcDescListSize(p->ipmp_tools, &size);
	return size;
}
static M4Err WriteIPMPX_GetToolsResponse(BitStream *bs, IPMPX_Data *_p)
{
	u32 i;
	IPMPX_GetToolsResponse *p = (IPMPX_GetToolsResponse *)_p;

	for (i=0; i<ChainGetCount(p->ipmp_tools); i++) {
		Descriptor *desc = ChainGetEntry(p->ipmp_tools, i);
		WriteDesc(bs, desc);
	}
	return M4OK;
}
static IPMPX_Data *NewIPMPX_ParametricDescription()
{
	IPMPX_ParametricDescription *ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_ParametricDescription), IPMPX_ParametricDescription_Tag);
	ptr->descriptions = NewChain();
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_ParametricDescription(IPMPX_Data *_p)
{
	IPMPX_ParametricDescription *p = (IPMPX_ParametricDescription *)_p;
	IPMPX_DELETE_ARRAY(p->descriptionComment);
	while (ChainGetCount(p->descriptions)) {
		IPMPX_ParametricDescriptionItem *it = ChainGetEntry(p->descriptions, 0);
		ChainDeleteEntry(p->descriptions, 0);
		IPMPX_DELETE_ARRAY(it->main_class);
		IPMPX_DELETE_ARRAY(it->subClass);
		IPMPX_DELETE_ARRAY(it->typeData);
		IPMPX_DELETE_ARRAY(it->type);
		IPMPX_DELETE_ARRAY(it->addedData);
		free(it);
	}
	DeleteChain(p->descriptions);
	free(p);
}
static M4Err ReadIPMPX_ParametricDescription(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	u32 count;
	IPMPX_ParametricDescription *p = (IPMPX_ParametricDescription *)_p;
	p->descriptionComment = IPMPX_GetByteArray(bs);
	p->majorVersion = BS_ReadInt(bs, 8);
	p->minorVersion = BS_ReadInt(bs, 8);
	count = BS_ReadInt(bs, 32);
	while (count) {
		IPMPX_ParametricDescriptionItem *it = malloc(sizeof(IPMPX_ParametricDescriptionItem));
		ChainAddEntry(p->descriptions, it);
		count--;
		it->main_class = IPMPX_GetByteArray(bs);
		it->subClass = IPMPX_GetByteArray(bs);
		it->typeData = IPMPX_GetByteArray(bs);
		it->type = IPMPX_GetByteArray(bs);
		it->addedData = IPMPX_GetByteArray(bs);
	}
	return M4OK;
}
static u32 SizeIPMPX_ParametricDescription(IPMPX_Data *_p)
{
	u32 size, i;
	IPMPX_ParametricDescription *p = (IPMPX_ParametricDescription *)_p;
	size = IPMPX_GetByteArraySize(p->descriptionComment);
	size += 6;
	for (i=0; i<ChainGetCount(p->descriptions); i++) {
		IPMPX_ParametricDescriptionItem *it = ChainGetEntry(p->descriptions, i);
		size += IPMPX_GetByteArraySize(it->main_class);
		size += IPMPX_GetByteArraySize(it->subClass);
		size += IPMPX_GetByteArraySize(it->typeData);
		size += IPMPX_GetByteArraySize(it->type);
		size += IPMPX_GetByteArraySize(it->addedData);
	}
	return size;
}
static u32 WriteIPMPX_ParametricDescription(BitStream *bs, IPMPX_Data *_p)
{
	u32 i;
	IPMPX_ParametricDescription *p = (IPMPX_ParametricDescription *)_p;
	IPMPX_WriteByteArray(bs, p->descriptionComment);
	BS_WriteInt(bs, p->majorVersion, 8);
	BS_WriteInt(bs, p->minorVersion, 8);
	BS_WriteInt(bs, ChainGetCount(p->descriptions), 32);

	for (i=0; i<ChainGetCount(p->descriptions); i++) {
		IPMPX_ParametricDescriptionItem *it = ChainGetEntry(p->descriptions, i);
		IPMPX_WriteByteArray(bs, it->main_class);
		IPMPX_WriteByteArray(bs, it->subClass);
		IPMPX_WriteByteArray(bs, it->typeData);
		IPMPX_WriteByteArray(bs, it->type);
		IPMPX_WriteByteArray(bs, it->addedData);
	}
	return M4OK;
}
static IPMPX_Data *NewIPMPX_ToolParamCapabilitiesQuery()
{
	IPMPX_ToolParamCapabilitiesQuery*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_ToolParamCapabilitiesQuery), IPMPX_ToolParamCapabilitiesQuery_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_ToolParamCapabilitiesQuery(IPMPX_Data *_p)
{
	IPMPX_ToolParamCapabilitiesQuery *p = (IPMPX_ToolParamCapabilitiesQuery *)_p;
	IPMPX_DeleteData((IPMPX_Data *) p->description);
	free(p);
}
static M4Err ReadIPMPX_ToolParamCapabilitiesQuery(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	IPMPX_ToolParamCapabilitiesQuery *p = (IPMPX_ToolParamCapabilitiesQuery*)_p;
	return IPMPX_ParseData(bs, (IPMPX_Data **) &p->description);
}
static u32 SizeIPMPX_ToolParamCapabilitiesQuery(IPMPX_Data *_p)
{
	IPMPX_ToolParamCapabilitiesQuery *p = (IPMPX_ToolParamCapabilitiesQuery*)_p;
	return IPMPX_GetDataFullSize((IPMPX_Data *) p->description);
}
static M4Err WriteIPMPX_ToolParamCapabilitiesQuery(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_ToolParamCapabilitiesQuery *p = (IPMPX_ToolParamCapabilitiesQuery*)_p;
	return IPMPX_WriteData(bs, (IPMPX_Data *) p->description);
}
static IPMPX_Data *NewIPMPX_ToolParamCapabilitiesResponse()
{
	IPMPX_ToolParamCapabilitiesResponse*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_ToolParamCapabilitiesResponse), IPMPX_ToolParamCapabilitiesResponse_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_ToolParamCapabilitiesResponse(IPMPX_Data *_p)
{
	free(_p);
}
static M4Err ReadIPMPX_ToolParamCapabilitiesResponse(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	IPMPX_ToolParamCapabilitiesResponse *p = (IPMPX_ToolParamCapabilitiesResponse*)_p;
	p->capabilitiesSupported = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 7);
	return M4OK;
}
static u32 SizeIPMPX_ToolParamCapabilitiesResponse(IPMPX_Data *_p)
{
	return 1;
}
static M4Err WriteIPMPX_ToolParamCapabilitiesResponse(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_ToolParamCapabilitiesResponse*p = (IPMPX_ToolParamCapabilitiesResponse*)_p;
	BS_WriteInt(bs, p->capabilitiesSupported, 1);
	BS_WriteInt(bs, 0, 7);
	return M4OK;
}

static IPMPX_Data *NewIPMPX_ConnectTool()
{
	IPMPX_ConnectTool*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_ConnectTool), IPMPX_ConnectTool_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_ConnectTool(IPMPX_Data *_p)
{
	IPMPX_ConnectTool *p = (IPMPX_ConnectTool*)_p;
	if (p->toolDescriptor) OD_DeleteDescriptor((Descriptor **)&p->toolDescriptor);
	free(p);
}
static M4Err ReadIPMPX_ConnectTool(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	u32 dsize;
	IPMPX_ConnectTool*p = (IPMPX_ConnectTool*)_p;
	return ParseDescriptor(bs, (Descriptor **) &p->toolDescriptor, &dsize);
}
static u32 SizeIPMPX_ConnectTool(IPMPX_Data *_p)
{
	u32 size;
	IPMPX_ConnectTool*p = (IPMPX_ConnectTool*)_p;
	CalcSize((Descriptor *)p->toolDescriptor, &size);
	size += GetSizeFieldSize(size);
	return size;
}
static M4Err WriteIPMPX_ConnectTool(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_ConnectTool*p = (IPMPX_ConnectTool*)_p;
	WriteDesc(bs, (Descriptor *)p->toolDescriptor);
	return M4OK;
}
static IPMPX_Data *NewIPMPX_DisconnectTool()
{
	IPMPX_DisconnectTool*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_DisconnectTool), IPMPX_DisconnectTool_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_DisconnectTool(IPMPX_Data *_p)
{
	free(_p);
}
static M4Err ReadIPMPX_DisconnectTool(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	IPMPX_DisconnectTool*p = (IPMPX_DisconnectTool*)_p;
	p->IPMP_ToolContextID = BS_ReadInt(bs, 32);
	return M4OK;
}
static u32 SizeIPMPX_DisconnectTool(IPMPX_Data *_p)
{
	return 4;
}
static M4Err WriteIPMPX_DisconnectTool(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_DisconnectTool*p = (IPMPX_DisconnectTool*)_p;
	BS_WriteInt(bs, p->IPMP_ToolContextID, 32);
	return M4OK;
}

static IPMPX_Data *NewIPMPX_GetToolContext()
{
	IPMPX_GetToolContext*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_GetToolContext), IPMPX_GetToolContext_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_GetToolContext(IPMPX_Data *_p)
{
	free(_p);
}
static M4Err ReadIPMPX_GetToolContext(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	Bool has_idex;
	IPMPX_GetToolContext*p = (IPMPX_GetToolContext*)_p;
	p->scope = BS_ReadInt(bs, 3);
	has_idex = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 4);
	if (has_idex) p->IPMP_DescriptorIDEx = BS_ReadInt(bs, 16);

	return M4OK;
}
static u32 SizeIPMPX_GetToolContext(IPMPX_Data *_p)
{
	IPMPX_GetToolContext*p = (IPMPX_GetToolContext*)_p;
	return p->IPMP_DescriptorIDEx ? 3 : 1;
}
static M4Err WriteIPMPX_GetToolContext(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_GetToolContext*p = (IPMPX_GetToolContext*)_p;
	BS_WriteInt(bs, p->scope, 3);
	BS_WriteInt(bs, p->IPMP_DescriptorIDEx ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 4);
	if (p->IPMP_DescriptorIDEx) BS_WriteInt(bs, p->IPMP_DescriptorIDEx, 16);
	return M4OK;
}
static IPMPX_Data *NewIPMPX_GetToolContextResponse()
{
	IPMPX_GetToolContextResponse*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_GetToolContextResponse), IPMPX_GetToolContextResponse_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_GetToolContextResponse(IPMPX_Data *_p)
{
	free(_p);
}
static M4Err ReadIPMPX_GetToolContextResponse(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	Bool has_esid;
	IPMPX_GetToolContextResponse*p = (IPMPX_GetToolContextResponse*)_p;
	has_esid = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 5);
	p->OD_ID = BS_ReadInt(bs, 10);
	if (has_esid) p->ESD_ID = BS_ReadInt(bs, 16);
	p->IPMP_ToolContextID = BS_ReadInt(bs, 32);
	return M4OK;
}
static u32 SizeIPMPX_GetToolContextResponse(IPMPX_Data *_p)
{
	IPMPX_GetToolContextResponse*p = (IPMPX_GetToolContextResponse*)_p;
	return p->ESD_ID ? 8 : 6;
}
static M4Err WriteIPMPX_GetToolContextResponse(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_GetToolContextResponse*p = (IPMPX_GetToolContextResponse*)_p;
	BS_WriteInt(bs, p->ESD_ID ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 5);
	BS_WriteInt(bs, p->OD_ID, 10);
	if (p->ESD_ID) BS_WriteInt(bs, p->ESD_ID, 16);
	BS_WriteInt(bs, p->IPMP_ToolContextID, 32);
	return M4OK;
}


static IPMPX_Data *NewIPMPX_AddToolNotificationListener()
{
	IPMPX_AddToolNotificationListener*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_AddToolNotificationListener), IPMPX_AddToolNotificationListener_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_AddToolNotificationListener(IPMPX_Data *_p)
{
	free(_p);
}
static M4Err ReadIPMPX_AddToolNotificationListener(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	u32 i;
	IPMPX_AddToolNotificationListener*p = (IPMPX_AddToolNotificationListener*)_p;
	p->scope = BS_ReadInt(bs, 3);
	BS_ReadInt(bs, 5);
	p->eventTypeCount = BS_ReadInt(bs, 8);
	for (i=0;i<p->eventTypeCount; i++) p->eventType[i] = BS_ReadInt(bs, 8);
	return M4OK;
}
static u32 SizeIPMPX_AddToolNotificationListener(IPMPX_Data *_p)
{
	IPMPX_AddToolNotificationListener*p = (IPMPX_AddToolNotificationListener*)_p;
	return p->eventTypeCount + 2;
}
static M4Err WriteIPMPX_AddToolNotificationListener(BitStream *bs, IPMPX_Data *_p)
{
	u32 i;
	IPMPX_AddToolNotificationListener*p = (IPMPX_AddToolNotificationListener*)_p;
	BS_WriteInt(bs, p->scope, 3);
	BS_WriteInt(bs, 0, 5);
	BS_WriteInt(bs, p->eventTypeCount, 8);
	for (i=0;i<p->eventTypeCount; i++) BS_WriteInt(bs, p->eventType[i], 8);
	return M4OK;
}
static IPMPX_Data *NewIPMPX_RemoveToolNotificationListener()
{
	IPMPX_RemoveToolNotificationListener*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_RemoveToolNotificationListener), IPMPX_RemoveToolNotificationListener_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_RemoveToolNotificationListener(IPMPX_Data *_p)
{
	free(_p);
}
static M4Err ReadIPMPX_RemoveToolNotificationListener(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	u32 i;
	IPMPX_RemoveToolNotificationListener*p = (IPMPX_RemoveToolNotificationListener*)_p;
	p->eventTypeCount = BS_ReadInt(bs, 8);
	for (i=0;i<p->eventTypeCount; i++) p->eventType[i] = BS_ReadInt(bs, 8);
	return M4OK;
}
static u32 SizeIPMPX_RemoveToolNotificationListener(IPMPX_Data *_p)
{
	IPMPX_RemoveToolNotificationListener*p = (IPMPX_RemoveToolNotificationListener*)_p;
	return p->eventTypeCount + 1;
}
static M4Err WriteIPMPX_RemoveToolNotificationListener(BitStream *bs, IPMPX_Data *_p)
{
	u32 i;
	IPMPX_RemoveToolNotificationListener*p = (IPMPX_RemoveToolNotificationListener*)_p;
	BS_WriteInt(bs, p->eventTypeCount, 8);
	for (i=0;i<p->eventTypeCount; i++) BS_WriteInt(bs, p->eventType[i], 8);
	return M4OK;
}

static IPMPX_Data *NewIPMPX_NotifyToolEvent()
{
	IPMPX_NotifyToolEvent*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_NotifyToolEvent), IPMPX_NotifyToolEvent_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_NotifyToolEvent(IPMPX_Data *_p)
{
	free(_p);
}
static M4Err ReadIPMPX_NotifyToolEvent(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	Bool has_id;
	IPMPX_NotifyToolEvent*p = (IPMPX_NotifyToolEvent*)_p;
	has_id = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 7);
	if (has_id) {
		p->OD_ID = BS_ReadInt(bs, 10);
		BS_ReadInt(bs, 6);
		p->ESD_ID = BS_ReadInt(bs, 16);
	}
	p->eventType = BS_ReadInt(bs, 8);
	p->IPMP_ToolContextID = BS_ReadInt(bs, 32);
	return M4OK;
}
static u32 SizeIPMPX_NotifyToolEvent(IPMPX_Data *_p)
{
	IPMPX_NotifyToolEvent*p = (IPMPX_NotifyToolEvent*)_p;
	return (p->OD_ID || p->ESD_ID) ? 10 : 6;
}
static M4Err WriteIPMPX_NotifyToolEvent(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_NotifyToolEvent*p = (IPMPX_NotifyToolEvent*)_p;
	BS_WriteInt(bs, (p->OD_ID || p->ESD_ID) ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 7);
	if (p->OD_ID || p->ESD_ID) {
		BS_WriteInt(bs, p->OD_ID, 10);
		BS_WriteInt(bs, 0, 6);
		BS_WriteInt(bs, p->ESD_ID, 16);
	}
	BS_WriteInt(bs, p->eventType, 8);
	BS_WriteInt(bs, p->IPMP_ToolContextID, 32);
	return M4OK;
}
static IPMPX_Data *NewIPMPX_CanProcess()
{
	IPMPX_CanProcess*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_CanProcess), IPMPX_CanProcess_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_CanProcess(IPMPX_Data *_p)
{
	free(_p);
}
static M4Err ReadIPMPX_CanProcess(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	IPMPX_CanProcess*p = (IPMPX_CanProcess*)_p;
	p->canProcess = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 7);
	return M4OK;
}
static u32 SizeIPMPX_CanProcess(IPMPX_Data *_p)
{
	return 1;
}
static M4Err WriteIPMPX_CanProcess(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_CanProcess*p = (IPMPX_CanProcess*)_p;
	BS_WriteInt(bs, p->canProcess ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 7);
	return M4OK;
}
static IPMPX_Data *NewIPMPX_OpaqueData(u8 tag)
{
	IPMPX_OpaqueData*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_OpaqueData), tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_OpaqueData(IPMPX_Data *_p)
{
	IPMPX_OpaqueData *p = (IPMPX_OpaqueData*)_p;
	IPMPX_DELETE_ARRAY(p->opaqueData);
	free(p);
}
static M4Err ReadIPMPX_OpaqueData(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	IPMPX_OpaqueData*p = (IPMPX_OpaqueData*)_p;
	p->opaqueData = IPMPX_GetByteArray(bs);
	return M4OK;
}
static u32 SizeIPMPX_OpaqueData(IPMPX_Data *_p)
{
	IPMPX_OpaqueData*p = (IPMPX_OpaqueData*)_p;
	return IPMPX_GetByteArraySize(p->opaqueData);
}
static M4Err WriteIPMPX_OpaqueData(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_OpaqueData*p = (IPMPX_OpaqueData*)_p;
	IPMPX_WriteByteArray(bs, p->opaqueData);
	return M4OK;
}
static IPMPX_Data *NewIPMPX_KeyData()
{
	IPMPX_KeyData*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_KeyData), IPMPX_KeyData_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_KeyData(IPMPX_Data *_p)
{
	IPMPX_KeyData*p = (IPMPX_KeyData*)_p;
	IPMPX_DELETE_ARRAY(p->keyBody);
	IPMPX_DELETE_ARRAY(p->OpaqueData);
	free(p);
}
static M4Err ReadIPMPX_KeyData(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	IPMPX_KeyData*p = (IPMPX_KeyData*)_p;
	p->keyBody = IPMPX_GetByteArray(bs);
	p->flags = 0;
	if (BS_ReadInt(bs, 1)) p->flags |= 1;
	if (BS_ReadInt(bs, 1)) p->flags |= 1<<1;
	if (BS_ReadInt(bs, 1)) p->flags |= 1<<2;
	if (BS_ReadInt(bs, 1)) p->flags |= 1<<3;
	BS_ReadInt(bs, 4);
	if (p->flags & (1)) p->startDTS = BS_ReadLongInt(bs, 64);
	if (p->flags & (1<<1)) p->startPacketID = BS_ReadInt(bs, 32);
	if (p->flags & (1<<2)) p->expireDTS = BS_ReadLongInt(bs, 64);
	if (p->flags & (1<<3)) p->expirePacketID = BS_ReadInt(bs, 32);
	p->OpaqueData = IPMPX_GetByteArray(bs);
	return M4OK;
}
static u32 SizeIPMPX_KeyData(IPMPX_Data *_p)
{
	u32 size = 0;
	IPMPX_KeyData*p = (IPMPX_KeyData*)_p;
	size += 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 += IPMPX_GetByteArraySize(p->OpaqueData);
	return size;
}
static M4Err WriteIPMPX_KeyData(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_KeyData*p = (IPMPX_KeyData*)_p;
	IPMPX_WriteByteArray(bs, p->keyBody);
	BS_WriteInt(bs, (p->flags & (1)) ? 1 : 0, 1);
	BS_WriteInt(bs, (p->flags & (1<<1)) ? 1 : 0, 1);
	BS_WriteInt(bs, (p->flags & (1<<2)) ? 1 : 0, 1);
	BS_WriteInt(bs, (p->flags & (1<<3)) ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 4);
	if (p->flags & (1)) BS_WriteLongInt(bs, p->startDTS, 64);
	if (p->flags & (1<<1)) BS_WriteInt(bs, p->startPacketID, 32);
	if (p->flags & (1<<2)) BS_WriteLongInt(bs, p->expireDTS, 64);
	if (p->flags & (1<<3)) BS_WriteInt(bs, p->expirePacketID, 32);
	IPMPX_WriteByteArray(bs, p->OpaqueData);
	return M4OK;
}

static IPMPX_Data *NewIPMPX_SelectiveDecryptionInit()
{
	IPMPX_SelectiveDecryptionInit*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_SelectiveDecryptionInit), IPMPX_SelectiveDecryptionInit_Tag);
	ptr->SelEncBuffer = NewChain();
	ptr->SelEncFields = NewChain();
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_SelectiveDecryptionInit(IPMPX_Data *_p)
{
	IPMPX_SelectiveDecryptionInit*p = (IPMPX_SelectiveDecryptionInit*)_p;
	while (ChainGetCount(p->SelEncBuffer)) {
		IPMPX_SelEncBuffer *sb = ChainGetEntry(p->SelEncBuffer, 0);
		ChainDeleteEntry(p->SelEncBuffer, 0);
		IPMPX_DELETE_ARRAY(sb->Stream_Cipher_Specific_Init_Info);
		free(sb);
	}
	DeleteChain(p->SelEncBuffer);
	while (ChainGetCount(p->SelEncFields)) {
		IPMPX_SelEncField*sf = ChainGetEntry(p->SelEncFields, 0);
		ChainDeleteEntry(p->SelEncFields, 0);
		IPMPX_DELETE_ARRAY(sf->shuffleSpecificInfo);
		if (sf->mappingTable) free(sf->mappingTable);
		free(sf);
	}
	DeleteChain(p->SelEncFields);
	if (p->RLE_Data) free(p->RLE_Data);
	free(p);
}
static M4Err ReadIPMPX_SelectiveDecryptionInit(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	u32 count, i;
	Bool is_spec;
	IPMPX_SelectiveDecryptionInit*p = (IPMPX_SelectiveDecryptionInit*)_p;
	p->mediaTypeExtension = BS_ReadInt(bs, 8);
	p->mediaTypeIndication = BS_ReadInt(bs, 8);
	p->profileLevelIndication = BS_ReadInt(bs, 8);
	p->compliance = BS_ReadInt(bs, 8);
	count = BS_ReadInt(bs, 8);
	while (count) {
		Bool is_block;
		IPMPX_SelEncBuffer *sb;
		SAFEALLOC(sb, sizeof(IPMPX_SelEncBuffer));
		ChainAddEntry(p->SelEncBuffer, sb);
		count--;
		BS_ReadData(bs, (unsigned char *) sb->cipher_Id, 16);
		sb->syncBoundary = BS_ReadInt(bs, 8);
		is_block = BS_ReadInt(bs, 1);
		BS_ReadInt(bs, 7);
		if (is_block) {
			sb->mode = BS_ReadInt(bs, 8);
			sb->blockSize = BS_ReadInt(bs, 16);
			sb->keySize = BS_ReadInt(bs, 16);
		} else {
			sb->Stream_Cipher_Specific_Init_Info = IPMPX_GetByteArray(bs);
		}
	}
	is_spec = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 7);
	if (is_spec) {
		Bool is_map;
		count = BS_ReadInt(bs, 8);
		while (count) {
			IPMPX_SelEncField *sf;
			SAFEALLOC(sf, sizeof(IPMPX_SelEncField));
			ChainAddEntry(p->SelEncFields, sf);
			count--;
			sf->field_Id = BS_ReadInt(bs, 8);
			sf->field_Scope = BS_ReadInt(bs, 3);
			BS_ReadInt(bs, 5);
			sf->buf = BS_ReadInt(bs, 8);
			is_map = BS_ReadInt(bs, 1);
			BS_ReadInt(bs, 7);
			if (is_map) {
				Bool sendMapTable = BS_ReadInt(bs, 1);
				Bool isShuffled = BS_ReadInt(bs, 1);
				BS_ReadInt(bs, 6);
				if (sendMapTable) {
					sf->mappingTableSize = BS_ReadInt(bs, 16);
					sf->mappingTable = malloc(sizeof(u16) * sf->mappingTableSize);
					for (i=0; i<sf->mappingTableSize; i++) sf->mappingTable[i] = BS_ReadInt(bs, 16);
				}
				if (isShuffled) sf->shuffleSpecificInfo = IPMPX_GetByteArray(bs);
			}
		}
	} else {
		p->RLE_DataLength = BS_ReadInt(bs, 16);
		p->RLE_Data = malloc(sizeof(u16)*p->RLE_DataLength);
		for (i=0; i<p->RLE_DataLength; i++) p->RLE_Data[i] = BS_ReadInt(bs, 16);
	}
	return M4OK;
}
static u32 SizeIPMPX_SelectiveDecryptionInit(IPMPX_Data *_p)
{
	u32 size, i;
	IPMPX_SelectiveDecryptionInit*p = (IPMPX_SelectiveDecryptionInit*)_p;

	size = 5;
	for (i=0; i<ChainGetCount(p->SelEncBuffer); i++) {
		IPMPX_SelEncBuffer *sb = ChainGetEntry(p->SelEncBuffer, i);
		size += 18;
		if (sb->Stream_Cipher_Specific_Init_Info) {
			size += 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<ChainGetCount(p->SelEncFields); i++) {
			IPMPX_SelEncField *sf = ChainGetEntry(p->SelEncFields, i);
			size += 4;
			if (sf->mappingTable || sf->shuffleSpecificInfo) {
				size += 1;
				if (sf->mappingTable) size += 2 + 2*sf->mappingTableSize;
				if (sf->shuffleSpecificInfo) size += IPMPX_GetByteArraySize(sf->shuffleSpecificInfo);
			}
		}
	}
	return size;
}
static M4Err WriteIPMPX_SelectiveDecryptionInit(BitStream *bs, IPMPX_Data *_p)
{
	u32 count, i;
	IPMPX_SelectiveDecryptionInit*p = (IPMPX_SelectiveDecryptionInit*)_p;

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

	count = ChainGetCount(p->SelEncBuffer);
	BS_WriteInt(bs, count, 8);
	for (i=0; i<count; i++) {
		IPMPX_SelEncBuffer *sb = ChainGetEntry(p->SelEncBuffer, i);
		BS_WriteData(bs, (unsigned char *) sb->cipher_Id, 16);
		BS_WriteInt(bs, sb->syncBoundary, 8);
		BS_WriteInt(bs, sb->Stream_Cipher_Specific_Init_Info ? 0 : 1, 1);
		BS_WriteInt(bs, 0, 7);
		if (sb->Stream_Cipher_Specific_Init_Info) {
			IPMPX_WriteByteArray(bs, sb->Stream_Cipher_Specific_Init_Info);
		} else {
			BS_WriteInt(bs, sb->mode, 8);
			BS_WriteInt(bs, sb->blockSize, 16);
			BS_WriteInt(bs, sb->keySize, 16);
		}
	}
	BS_WriteInt(bs, p->RLE_Data ? 0 : 1, 1);
	BS_WriteInt(bs, 0, 7);
	if (p->RLE_Data) {
		BS_WriteInt(bs, p->RLE_DataLength, 16);
		for (i=0; i<p->RLE_DataLength; i++) BS_WriteInt(bs, p->RLE_Data[i], 16);
	} else {
		count = ChainGetCount(p->SelEncFields);
		BS_WriteInt(bs, count, 8);
		for (i=0; i<count; i++) {
			IPMPX_SelEncField *sf = ChainGetEntry(p->SelEncFields, i);
			BS_WriteInt(bs, sf->field_Id, 8);
			BS_WriteInt(bs, sf->field_Scope, 3);
			BS_WriteInt(bs, 0, 5);
			BS_WriteInt(bs, sf->buf, 8);
			BS_WriteInt(bs, (sf->mappingTable || sf->shuffleSpecificInfo) ? 1 : 0, 1);
			BS_WriteInt(bs, 0, 7);
			if (sf->mappingTable || sf->shuffleSpecificInfo) {
				BS_WriteInt(bs, sf->mappingTable ? 1 : 0, 1);
				BS_WriteInt(bs, sf->shuffleSpecificInfo ? 1 : 0, 1);
				BS_WriteInt(bs, 0, 6);
				if (sf->mappingTable) {
					BS_WriteInt(bs, sf->mappingTableSize, 16);
					for (i=0; i<sf->mappingTableSize; i++) BS_WriteInt(bs, sf->mappingTable[i], 16);
				}
				if (sf->shuffleSpecificInfo) IPMPX_WriteByteArray(bs, sf->shuffleSpecificInfo);
			}
		}
	}
	return M4OK;
}
static IPMPX_Data *NewIPMPX_WatermarkingInit(u8 tag)
{
	IPMPX_WatermarkingInit *ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_WatermarkingInit), tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_WatermarkingInit(IPMPX_Data *_p)
{
	IPMPX_WatermarkingInit *p = (IPMPX_WatermarkingInit*)_p;
	if (p->wmPayload) free(p->wmPayload);
	if (p->opaqueData) free(p->opaqueData);
	free(p);
}
static M4Err ReadIPMPX_WatermarkingInit(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	Bool has_opaque_data;
	IPMPX_WatermarkingInit *p = (IPMPX_WatermarkingInit*)_p;

	p->inputFormat = BS_ReadInt(bs, 8);
	p->requiredOp = BS_ReadInt(bs, 4);
	has_opaque_data = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 3);
	if (p->inputFormat==0x01) {
		if (p->tag == IPMPX_AudioWatermarkingInit_Tag) {
			p->nChannels = BS_ReadInt(bs, 8);
			p->bitPerSample = BS_ReadInt(bs, 8);
			p->frequency = BS_ReadInt(bs, 32);
		} else {
			p->frame_horizontal_size = BS_ReadInt(bs, 16);
			p->frame_vertical_size = BS_ReadInt(bs, 16);
			p->chroma_format = BS_ReadInt(bs, 8);
		}
	}
	switch (p->requiredOp) {
	case WM_INSERT:
	case WM_REMARK:
		p->wmPayloadLen = BS_ReadInt(bs, 16);
		p->wmPayload = malloc(sizeof(u8) * p->wmPayloadLen);
		BS_ReadData(bs, p->wmPayload, p->wmPayloadLen);
		break;
	case WM_EXTRACT:
	case WM_DETECT_COMPRESSION:
		p->wmRecipientId = BS_ReadInt(bs, 16);
		break;
	}
	if (has_opaque_data) {
		p->opaqueDataSize = BS_ReadInt(bs, 16);
		p->opaqueData = malloc(sizeof(u8) * p->wmPayloadLen);
		BS_ReadData(bs, p->opaqueData, p->opaqueDataSize);
	}
	return M4OK;
}
static u32 SizeIPMPX_WatermarkingInit(IPMPX_Data *_p)
{
	u32 size;
	IPMPX_WatermarkingInit *p = (IPMPX_WatermarkingInit*)_p;
	size = 2;
	if (p->inputFormat==0x01) size += (p->tag == IPMPX_AudioWatermarkingInit_Tag) ? 6 : 5;
	switch (p->requiredOp) {
	case WM_INSERT:
	case WM_REMARK:
		size += 2+p->wmPayloadLen;
		break;
	case WM_EXTRACT:
	case WM_DETECT_COMPRESSION:
		size += 2;
		break;
	}
	if (p->opaqueData) size += p->opaqueDataSize + 2;
	return size;
}
static M4Err WriteIPMPX_WatermarkingInit(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_WatermarkingInit*p = (IPMPX_WatermarkingInit*)_p;

	BS_WriteInt(bs, p->inputFormat, 8);
	BS_WriteInt(bs, p->requiredOp, 4);
	BS_WriteInt(bs, p->opaqueData ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 3);
	if (p->inputFormat==0x01) {
		if (p->tag == IPMPX_AudioWatermarkingInit_Tag) {
			BS_WriteInt(bs, p->nChannels, 8);
			BS_WriteInt(bs, p->bitPerSample, 8);
			BS_WriteInt(bs, p->frequency, 32);
		} else {
			BS_WriteInt(bs, p->frame_horizontal_size, 16);
			BS_WriteInt(bs, p->frame_vertical_size, 16);
			BS_WriteInt(bs, p->chroma_format, 8);
		}
	}
	switch (p->requiredOp) {
	case WM_INSERT:
	case WM_REMARK:
		BS_WriteInt(bs, p->wmPayloadLen, 16);
		BS_WriteData(bs, p->wmPayload, p->wmPayloadLen);
		break;
	case WM_EXTRACT:
	case WM_DETECT_COMPRESSION:
		BS_WriteInt(bs, p->wmRecipientId, 16);
		break;
	}
	if (p->opaqueData) {
		BS_WriteInt(bs, p->opaqueDataSize, 16);
		BS_WriteData(bs, p->opaqueData, p->opaqueDataSize);
	}
	return M4OK;
}
static IPMPX_Data *NewIPMPX_SendWatermark(u8 tag)
{
	IPMPX_SendWatermark*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_SendWatermark), tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_SendWatermark(IPMPX_Data *_p)
{
	IPMPX_SendWatermark*p = (IPMPX_SendWatermark*)_p;
	IPMPX_DELETE_ARRAY(p->payload);
	IPMPX_DELETE_ARRAY(p->opaqueData);
	free(p);
}
static M4Err ReadIPMPX_SendWatermark(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	Bool has_op_data;
	IPMPX_SendWatermark *p = (IPMPX_SendWatermark*)_p;
	p->wm_status = BS_ReadInt(bs, 2);
	p->compression_status = BS_ReadInt(bs, 2);
	has_op_data = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 3);
	if (p->wm_status==WM_PAYLOAD) p->payload = IPMPX_GetByteArray(bs);
	if (has_op_data) p->opaqueData = IPMPX_GetByteArray(bs);
	return M4OK;
}
static u32 SizeIPMPX_SendWatermark(IPMPX_Data *_p)
{
	u32 size;
	IPMPX_SendWatermark *p = (IPMPX_SendWatermark*)_p;
	size = 1;
	if (p->payload) size += IPMPX_GetByteArraySize(p->payload);
	if (p->opaqueData) size += IPMPX_GetByteArraySize(p->opaqueData);
	return size;
}
static M4Err WriteIPMPX_SendWatermark(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_SendWatermark*p = (IPMPX_SendWatermark*)_p;
	if (p->payload) p->wm_status = WM_PAYLOAD;
	BS_WriteInt(bs, p->wm_status, 2);
	BS_WriteInt(bs, p->compression_status, 2);
	BS_WriteInt(bs, p->opaqueData ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 3);
	if (p->payload) IPMPX_WriteByteArray(bs, p->payload);
	if (p->opaqueData) IPMPX_WriteByteArray(bs, p->opaqueData);
	return M4OK;
}

static IPMPX_Data *NewIPMPX_ToolAPI_Config()
{
	IPMPX_ToolAPI_Config*ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_ToolAPI_Config), IPMPX_ToolAPI_Config_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_ToolAPI_Config(IPMPX_Data *_p)
{
	IPMPX_ToolAPI_Config*p = (IPMPX_ToolAPI_Config*)_p;
	IPMPX_DELETE_ARRAY(p->opaqueData);
	free(p);
}
static M4Err ReadIPMPX_ToolAPI_Config(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	Bool has_i, has_m;
	IPMPX_ToolAPI_Config*p = (IPMPX_ToolAPI_Config*)_p;
	has_i = BS_ReadInt(bs, 1);
	has_m = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 6);
	if (has_i) p->Instantiation_API_ID = BS_ReadInt(bs, 32);
	if (has_m) p->Messaging_API_ID = BS_ReadInt(bs, 32);
	p->opaqueData = IPMPX_GetByteArray(bs);
	return M4OK;
}
static u32 SizeIPMPX_ToolAPI_Config(IPMPX_Data *_p)
{
	u32 size;
	IPMPX_ToolAPI_Config *p = (IPMPX_ToolAPI_Config*)_p;
	size = 1;
	if (p->Instantiation_API_ID) size += 4;
	if (p->Messaging_API_ID ) size += 4;
	size += IPMPX_GetByteArraySize(p->opaqueData);
	return size;
}
static M4Err WriteIPMPX_ToolAPI_Config(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_ToolAPI_Config*p = (IPMPX_ToolAPI_Config*)_p;
	BS_WriteInt(bs, p->Instantiation_API_ID ? 1 : 0, 1);
	BS_WriteInt(bs, p->Messaging_API_ID ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 6);
	if (p->Instantiation_API_ID) BS_WriteInt(bs, p->Instantiation_API_ID, 32);
	if (p->Messaging_API_ID) BS_WriteInt(bs, p->Messaging_API_ID, 32);
	IPMPX_WriteByteArray(bs, p->opaqueData);
	return M4OK;
}

static IPMPX_Data *NewIPMPX_ISMACryp()
{
	IPMPX_ISMACryp *ptr;
	IPMPX_DATA_ALLOC(ptr, sizeof(IPMPX_ISMACryp), IPMPX_ISMACryp_Tag);
	return (IPMPX_Data *) ptr;
}
static void DelIPMPX_ISMACryp(IPMPX_Data *_p)
{
	IPMPX_ISMACryp*p = (IPMPX_ISMACryp*)_p;
	free(p);
}
static M4Err ReadIPMPX_ISMACryp(BitStream *bs, IPMPX_Data *_p, u32 size)
{
	IPMPX_ISMACryp*p = (IPMPX_ISMACryp*)_p;
	p->cryptoSuite = BS_ReadInt(bs, 8);
	p->IV_length = BS_ReadInt(bs, 8);
	p->use_selective_encryption = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 7);
	p->key_indicator_length = BS_ReadInt(bs, 8);
	return M4OK;
}
static u32 SizeIPMPX_ISMACryp(IPMPX_Data *_p)
{
	return 4;
}
static M4Err WriteIPMPX_ISMACryp(BitStream *bs, IPMPX_Data *_p)
{
	IPMPX_ISMACryp*p = (IPMPX_ISMACryp*)_p;
	BS_WriteInt(bs, p->cryptoSuite, 8);
	BS_WriteInt(bs, p->IV_length, 8);
	BS_WriteInt(bs, p->use_selective_encryption ? 1 : 0, 1);
	BS_WriteInt(bs, 0, 7);
	BS_WriteInt(bs, p->key_indicator_length, 8);
	return M4OK;
}

IPMPX_Data *IPMPX_NewData(u8 tag)
{
	switch (tag) {
	case IPMPX_RightsData_Tag:
	case IPMPX_OpaqueData_Tag: return NewIPMPX_OpaqueData(tag);
	case IPMPX_KeyData_Tag: return NewIPMPX_KeyData();
	case IPMPX_SecureContainer_Tag: return NewIPMPX_SecureContainer();
	case IPMPX_AddToolNotificationListener_Tag: return NewIPMPX_AddToolNotificationListener();
	case IPMPX_RemoveToolNotificationListener_Tag: return NewIPMPX_RemoveToolNotificationListener();
	case IPMPX_InitAuthentication_Tag: return NewIPMPX_InitAuthentication();
	case IPMPX_MutualAuthentication_Tag: return NewIPMPX_MutualAuthentication();
	case IPMPX_ParametricDescription_Tag: return NewIPMPX_ParametricDescription();
	case IPMPX_ToolParamCapabilitiesQuery_Tag: return NewIPMPX_ToolParamCapabilitiesQuery();
	case IPMPX_ToolParamCapabilitiesResponse_Tag: return NewIPMPX_ToolParamCapabilitiesResponse();
	case IPMPX_GetToolsResponse_Tag: return NewIPMPX_GetToolsResponse();
	case IPMPX_GetToolContext_Tag: return NewIPMPX_GetToolContext();
	case IPMPX_GetToolContextResponse_Tag: return NewIPMPX_GetToolContextResponse();
	case IPMPX_ConnectTool_Tag: return NewIPMPX_ConnectTool();
	case IPMPX_DisconnectTool_Tag: return NewIPMPX_DisconnectTool();
	case IPMPX_NotifyToolEvent_Tag: return NewIPMPX_NotifyToolEvent();
	case IPMPX_CanProcess_Tag: return NewIPMPX_CanProcess();
	case IPMPX_TrustSecurityMetadata_Tag: return NewIPMPX_TrustSecurityMetadata();
	case IPMPX_ISMACryp_Tag: return NewIPMPX_ISMACryp();
	case IPMPX_GetTools_Tag: 
	{
		IPMPX_Data *p;
		IPMPX_DATA_ALLOC(p, sizeof(IPMPX_Data), IPMPX_GetTools_Tag);
		return p;
	}
	case IPMPX_TrustedTool_Tag:
	{
		IPMPX_TrustedTool *p;
		IPMPX_DATA_ALLOC(p, sizeof(IPMPX_TrustedTool), IPMPX_TrustedTool_Tag);
		p->trustSpecifications = NewChain();
		return (IPMPX_Data *)p;
	}
	case IPMPX_TrustSpecification_Tag:
	{
		IPMPX_TrustSpecification *p;
		IPMPX_DATA_ALLOC(p, sizeof(IPMPX_TrustSpecification), IPMPX_TrustSpecification_Tag);
		return (IPMPX_Data *)p;
	}
	case IPMPX_ToolAPI_Config_Tag: return NewIPMPX_ToolAPI_Config();
	case IPMPX_SelectiveDecryptionInit_Tag: return NewIPMPX_SelectiveDecryptionInit();
	case IPMPX_AudioWatermarkingInit_Tag: 
	case IPMPX_VideoWatermarkingInit_Tag: 
		return NewIPMPX_WatermarkingInit(tag);
	case IPMPX_SendAudioWatermark_Tag: 
	case IPMPX_SendVideoWatermark_Tag: 
		return NewIPMPX_SendWatermark(tag);

	case IPMPX_AlgorithmDescr_Tag:
	{
		IPMPX_AUTH_AlgorithmDescriptor *p = malloc(sizeof(IPMPX_AUTH_AlgorithmDescriptor));
		if (!p) return NULL;
		memset(p, 0, sizeof(IPMPX_AUTH_AlgorithmDescriptor));
		p->tag = IPMPX_AlgorithmDescr_Tag;
		return (IPMPX_Data *) p;
	}
	case IPMPX_KeyDescr_Tag:
	{
		IPMPX_AUTH_KeyDescriptor *p = malloc(sizeof(IPMPX_AUTH_KeyDescriptor));
		if (!p) return NULL;
		memset(p, 0, sizeof(IPMPX_AUTH_KeyDescriptor));
		p->tag = IPMPX_KeyDescr_Tag;
		return (IPMPX_Data *) p;
	}

	case IPMPX_ParamDescrItem_Tag:
	{
		IPMPX_AUTH_KeyDescriptor *p = malloc(sizeof(IPMPX_ParametricDescriptionItem));
		if (!p) return NULL;
		memset(p, 0, sizeof(IPMPX_ParametricDescriptionItem));
		p->tag = IPMPX_ParamDescrItem_Tag;
		return (IPMPX_Data *) p;
	}
	case IPMPX_SelEncBuffer_Tag:
	{
		IPMPX_SelEncBuffer*p = malloc(sizeof(IPMPX_SelEncBuffer));
		if (!p) return NULL;
		memset(p, 0, sizeof(IPMPX_SelEncBuffer));
		p->tag = IPMPX_SelEncBuffer_Tag;
		return (IPMPX_Data *) p;
	}
	case IPMPX_SelEncField_Tag:
	{
		IPMPX_SelEncField*p = malloc(sizeof(IPMPX_SelEncField));
		if (!p) return NULL;
		memset(p, 0, sizeof(IPMPX_SelEncField));
		p->tag = IPMPX_SelEncField_Tag;
		return (IPMPX_Data *) p;
	}

/*	
	case IPMPX_UserQuery_Tag: return NewIPMPX_UserQuery();
	case IPMPX_UserQueryResponse_Tag: return NewIPMPX_UserQueryResponse();
*/
	default: return NULL;
	}

}

void IPMPX_DeleteData(IPMPX_Data *_p)
{
	if (!_p) return;
	switch (_p->tag) {
	case IPMPX_RightsData_Tag:
	case IPMPX_OpaqueData_Tag: DelIPMPX_OpaqueData(_p); return;
	case IPMPX_KeyData_Tag: DelIPMPX_KeyData(_p); return;
	case IPMPX_SecureContainer_Tag: DelIPMPX_SecureContainer(_p); return;
	case IPMPX_AddToolNotificationListener_Tag: DelIPMPX_AddToolNotificationListener(_p); return;
	case IPMPX_RemoveToolNotificationListener_Tag: DelIPMPX_RemoveToolNotificationListener(_p); return;
	case IPMPX_InitAuthentication_Tag: DelIPMPX_InitAuthentication(_p); return;
	case IPMPX_MutualAuthentication_Tag: DelIPMPX_MutualAuthentication(_p); return;
	case IPMPX_ParametricDescription_Tag: DelIPMPX_ParametricDescription(_p); return;
	case IPMPX_ToolParamCapabilitiesQuery_Tag: DelIPMPX_ToolParamCapabilitiesQuery(_p); return;
	case IPMPX_ToolParamCapabilitiesResponse_Tag: DelIPMPX_ToolParamCapabilitiesResponse(_p); return;
	case IPMPX_GetToolsResponse_Tag: DelIPMPX_GetToolsResponse(_p); return;
	case IPMPX_GetToolContext_Tag: DelIPMPX_GetToolContext(_p); return;
	case IPMPX_GetToolContextResponse_Tag: DelIPMPX_GetToolContextResponse(_p); return;
	case IPMPX_ConnectTool_Tag: DelIPMPX_ConnectTool(_p); return;
	case IPMPX_DisconnectTool_Tag: DelIPMPX_DisconnectTool(_p); return;
	case IPMPX_NotifyToolEvent_Tag: DelIPMPX_NotifyToolEvent(_p); return;
	case IPMPX_CanProcess_Tag: DelIPMPX_CanProcess(_p); return;
	case IPMPX_TrustSecurityMetadata_Tag: DelIPMPX_TrustSecurityMetadata(_p); return;
	case IPMPX_ToolAPI_Config_Tag: DelIPMPX_ToolAPI_Config(_p); return;
	case IPMPX_ISMACryp_Tag: DelIPMPX_ISMACryp(_p); return;
	case IPMPX_SelectiveDecryptionInit_Tag: DelIPMPX_SelectiveDecryptionInit(_p); return;
	case IPMPX_AudioWatermarkingInit_Tag: 
	case IPMPX_VideoWatermarkingInit_Tag: 
		DelIPMPX_WatermarkingInit(_p); return;
	case IPMPX_SendAudioWatermark_Tag: 
	case IPMPX_SendVideoWatermark_Tag: 
		DelIPMPX_SendWatermark(_p); return;

/*
	case IPMPX_UserQuery_Tag: DelIPMPX_UserQuery(_p); return;
	case IPMPX_UserQueryResponse_Tag: DelIPMPX_UserQueryResponse(_p); return;
*/
	case IPMPX_TrustedTool_Tag:
	{
		IPMPX_TrustedTool *p = (IPMPX_TrustedTool *)_p;
		while (ChainGetCount(p->trustSpecifications)) {
			IPMPX_Data *ts = ChainGetEntry(p->trustSpecifications, 0);
			ChainDeleteEntry(p->trustSpecifications, 0);
			IPMPX_DeleteData(ts);
		}
		DeleteChain(p->trustSpecifications);
		free(p);
		return;
	}
	case IPMPX_TrustSpecification_Tag:
	{
		IPMPX_TrustSpecification *p = (IPMPX_TrustSpecification *)_p;
		IPMPX_DELETE_ARRAY(p->CCTrustMetadata);
		free(p);
		return;
	}
	case IPMPX_ParamDescrItem_Tag:
	{
		IPMPX_ParametricDescriptionItem *p = (IPMPX_ParametricDescriptionItem*)_p;
		IPMPX_DELETE_ARRAY(p->main_class);
		IPMPX_DELETE_ARRAY(p->subClass);
		IPMPX_DELETE_ARRAY(p->typeData);
		IPMPX_DELETE_ARRAY(p->type);
		IPMPX_DELETE_ARRAY(p->addedData);
		free(p);
		return;
	}
	case IPMPX_AlgorithmDescr_Tag:
		_p->tag = IPMPX_AUTH_AlgorithmDescr_Tag;
		IPMPX_AUTH_Delete((IPMPX_Authentication*)_p);
		return;
	case IPMPX_KeyDescr_Tag:
		_p->tag = IPMPX_AUTH_KeyDescr_Tag;
		IPMPX_AUTH_Delete((IPMPX_Authentication*)_p);
		return;

	case IPMPX_SelEncBuffer_Tag:
	{
		IPMPX_SelEncBuffer*p = (IPMPX_SelEncBuffer*)_p;
		IPMPX_DELETE_ARRAY(p->Stream_Cipher_Specific_Init_Info);
		free(p);
		return;
	}
	case IPMPX_SelEncField_Tag:
	{
		IPMPX_SelEncField*p = (IPMPX_SelEncField*)_p;
		IPMPX_DELETE_ARRAY(p->shuffleSpecificInfo);
		if (p->mappingTable) free(p->mappingTable);
		free(p);
		return;
	}

	case IPMPX_GetTools_Tag:
	default: 
		free(_p);
		return;
	}
}

M4Err IPMPX_ReadData(BitStream *bs, IPMPX_Data *_p, u32 read)
{
	switch (_p->tag) {
	case IPMPX_RightsData_Tag:
	case IPMPX_OpaqueData_Tag: return ReadIPMPX_OpaqueData(bs, _p, read);
	case IPMPX_KeyData_Tag: return ReadIPMPX_KeyData(bs, _p, read);
	case IPMPX_SecureContainer_Tag: return ReadIPMPX_SecureContainer(bs, _p, read);
	case IPMPX_AddToolNotificationListener_Tag: return ReadIPMPX_AddToolNotificationListener(bs, _p, read);
	case IPMPX_RemoveToolNotificationListener_Tag: return ReadIPMPX_RemoveToolNotificationListener(bs, _p, read);
	case IPMPX_InitAuthentication_Tag: return ReadIPMPX_InitAuthentication(bs, _p, read);
	case IPMPX_MutualAuthentication_Tag: return ReadIPMPX_MutualAuthentication(bs, _p, read);
	case IPMPX_ParametricDescription_Tag: return ReadIPMPX_ParametricDescription(bs, _p, read);
	case IPMPX_ToolParamCapabilitiesQuery_Tag: return ReadIPMPX_ToolParamCapabilitiesQuery(bs, _p, read);
	case IPMPX_ToolParamCapabilitiesResponse_Tag: return ReadIPMPX_ToolParamCapabilitiesResponse(bs, _p, read);
	case IPMPX_GetToolsResponse_Tag: return ReadIPMPX_GetToolsResponse(bs, _p, read);
	case IPMPX_GetToolContext_Tag: return ReadIPMPX_GetToolContext(bs, _p, read);
	case IPMPX_GetToolContextResponse_Tag: return ReadIPMPX_GetToolContextResponse(bs, _p, read);
	case IPMPX_ConnectTool_Tag: return ReadIPMPX_ConnectTool(bs, _p, read);
	case IPMPX_DisconnectTool_Tag: return ReadIPMPX_DisconnectTool(bs, _p, read);
	case IPMPX_NotifyToolEvent_Tag: return ReadIPMPX_NotifyToolEvent(bs, _p, read);
	case IPMPX_CanProcess_Tag: return ReadIPMPX_CanProcess(bs, _p, read);
	case IPMPX_TrustSecurityMetadata_Tag: return ReadIPMPX_TrustSecurityMetadata(bs, _p, read);
	case IPMPX_ToolAPI_Config_Tag: return ReadIPMPX_ToolAPI_Config(bs, _p, read);
	case IPMPX_ISMACryp_Tag: return ReadIPMPX_ISMACryp(bs, _p, read);
	case IPMPX_SelectiveDecryptionInit_Tag: return ReadIPMPX_SelectiveDecryptionInit(bs, _p, read);
	case IPMPX_AudioWatermarkingInit_Tag: 
	case IPMPX_VideoWatermarkingInit_Tag: 
		return ReadIPMPX_WatermarkingInit(bs, _p, read);
	case IPMPX_SendAudioWatermark_Tag: 
	case IPMPX_SendVideoWatermark_Tag: 
		return ReadIPMPX_SendWatermark(bs, _p, read);

/*
	case IPMPX_UserQuery_Tag: return ReadIPMPX_UserQuery(bs, _p, read);
	case IPMPX_UserQueryResponse_Tag: return ReadIPMPX_UserQueryResponse(bs, _p, read);
*/
	case IPMPX_GetTools_Tag: return M4OK;
	default: return M4BadParam;
	}
}

u32 IPMPX_GetDataSize(IPMPX_Data *_p)
{
	switch (_p->tag) {
	case IPMPX_RightsData_Tag:
	case IPMPX_OpaqueData_Tag: return SizeIPMPX_OpaqueData(_p);
	case IPMPX_KeyData_Tag: return SizeIPMPX_KeyData(_p);
	case IPMPX_SecureContainer_Tag: return SizeIPMPX_SecureContainer(_p);
	case IPMPX_AddToolNotificationListener_Tag: return SizeIPMPX_AddToolNotificationListener(_p);
	case IPMPX_RemoveToolNotificationListener_Tag: return SizeIPMPX_RemoveToolNotificationListener(_p);
	case IPMPX_InitAuthentication_Tag: return SizeIPMPX_InitAuthentication(_p);
	case IPMPX_MutualAuthentication_Tag: return SizeIPMPX_MutualAuthentication(_p);
	case IPMPX_ParametricDescription_Tag: return SizeIPMPX_ParametricDescription(_p);
	case IPMPX_ToolParamCapabilitiesQuery_Tag: return SizeIPMPX_ToolParamCapabilitiesQuery(_p);
	case IPMPX_ToolParamCapabilitiesResponse_Tag: return SizeIPMPX_ToolParamCapabilitiesResponse(_p);
	case IPMPX_GetToolsResponse_Tag: return SizeIPMPX_GetToolsResponse(_p);
	case IPMPX_GetToolContext_Tag: return SizeIPMPX_GetToolContext(_p);
	case IPMPX_GetToolContextResponse_Tag: return SizeIPMPX_GetToolContextResponse(_p);
	case IPMPX_ConnectTool_Tag: return SizeIPMPX_ConnectTool(_p);
	case IPMPX_DisconnectTool_Tag: return SizeIPMPX_DisconnectTool(_p);
	case IPMPX_NotifyToolEvent_Tag: return SizeIPMPX_NotifyToolEvent(_p);
	case IPMPX_CanProcess_Tag: return SizeIPMPX_CanProcess(_p);
	case IPMPX_TrustSecurityMetadata_Tag: return SizeIPMPX_TrustSecurityMetadata(_p);
	case IPMPX_ToolAPI_Config_Tag: return SizeIPMPX_ToolAPI_Config(_p);
	case IPMPX_ISMACryp_Tag: return SizeIPMPX_ISMACryp(_p);
	case IPMPX_SelectiveDecryptionInit_Tag: return SizeIPMPX_SelectiveDecryptionInit(_p);
	case IPMPX_AudioWatermarkingInit_Tag: 
	case IPMPX_VideoWatermarkingInit_Tag: 
		return SizeIPMPX_WatermarkingInit(_p);
	case IPMPX_SendAudioWatermark_Tag: 
	case IPMPX_SendVideoWatermark_Tag: 
		return SizeIPMPX_SendWatermark(_p);

/*
	case IPMPX_UserQuery_Tag: return SizeIPMPX_UserQuery(_p);
	case IPMPX_UserQueryResponse_Tag: return SizeIPMPX_UserQueryResponse(_p);
*/
	case IPMPX_GetTools_Tag: return 0;
	default: return M4BadParam;
	}
}

M4Err IPMPX_WriteData(BitStream *bs, IPMPX_Data *_p)
{
	M4Err e;
	if (!_p) return M4OK;
	e = IPMPX_WriteBase(bs, _p);
	if (e) return e;
	switch (_p->tag) {
	case IPMPX_RightsData_Tag:
	case IPMPX_OpaqueData_Tag: return WriteIPMPX_OpaqueData(bs, _p);
	case IPMPX_KeyData_Tag: return WriteIPMPX_KeyData(bs, _p);
	case IPMPX_SecureContainer_Tag: return WriteIPMPX_SecureContainer(bs, _p);
	case IPMPX_AddToolNotificationListener_Tag: return WriteIPMPX_AddToolNotificationListener(bs, _p);
	case IPMPX_RemoveToolNotificationListener_Tag: return WriteIPMPX_RemoveToolNotificationListener(bs, _p);
	case IPMPX_InitAuthentication_Tag: return WriteIPMPX_InitAuthentication(bs, _p);
	case IPMPX_MutualAuthentication_Tag: return WriteIPMPX_MutualAuthentication(bs, _p);
	case IPMPX_ParametricDescription_Tag: return WriteIPMPX_ParametricDescription(bs, _p);
	case IPMPX_ToolParamCapabilitiesQuery_Tag: return WriteIPMPX_ToolParamCapabilitiesQuery(bs, _p);
	case IPMPX_ToolParamCapabilitiesResponse_Tag: return WriteIPMPX_ToolParamCapabilitiesResponse(bs, _p);
	case IPMPX_GetToolsResponse_Tag: return WriteIPMPX_GetToolsResponse(bs, _p);
	case IPMPX_GetToolContext_Tag: return WriteIPMPX_GetToolContext(bs, _p);
	case IPMPX_GetToolContextResponse_Tag: return WriteIPMPX_GetToolContextResponse(bs, _p);
	case IPMPX_ConnectTool_Tag: return WriteIPMPX_ConnectTool(bs, _p);
	case IPMPX_DisconnectTool_Tag: return WriteIPMPX_DisconnectTool(bs, _p);
	case IPMPX_NotifyToolEvent_Tag: return WriteIPMPX_NotifyToolEvent(bs, _p);
	case IPMPX_CanProcess_Tag: return WriteIPMPX_CanProcess(bs, _p);
	case IPMPX_TrustSecurityMetadata_Tag: return WriteIPMPX_TrustSecurityMetadata(bs, _p);
	case IPMPX_ToolAPI_Config_Tag: return WriteIPMPX_ToolAPI_Config(bs, _p);
	case IPMPX_ISMACryp_Tag: return WriteIPMPX_ISMACryp(bs, _p);
	case IPMPX_SelectiveDecryptionInit_Tag: return WriteIPMPX_SelectiveDecryptionInit(bs, _p);
	case IPMPX_AudioWatermarkingInit_Tag: 
	case IPMPX_VideoWatermarkingInit_Tag: 
		return WriteIPMPX_WatermarkingInit(bs, _p);
	case IPMPX_SendAudioWatermark_Tag: 
	case IPMPX_SendVideoWatermark_Tag: 
		return WriteIPMPX_SendWatermark(bs, _p);

/*
	case IPMPX_UserQuery_Tag: return WriteIPMPX_UserQuery(bs, _p);
	case IPMPX_UserQueryResponse_Tag: return WriteIPMPX_UserQueryResponse(bs, _p);
*/
	case IPMPX_GetTools_Tag: return M4OK;
	default: return M4BadParam;
	}
}

