/*
 *			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>

void IPMPX_AUTH_Delete(IPMPX_Authentication *auth);


u8 IPMPX_GetTagByName(char *dataName)
{
	if (!stricmp(dataName, "IPMP_KeyData")) return IPMPX_KeyData_Tag;
	else if (!stricmp(dataName, "IPMP_RightsData")) return IPMPX_RightsData_Tag;
	else if (!stricmp(dataName, "IPMP_OpaqueData")) return IPMPX_OpaqueData_Tag;
	else if (!stricmp(dataName, "IPMP_SecureContainer")) return IPMPX_SecureContainer_Tag;
	else if (!stricmp(dataName, "IPMP_InitAuthentication")) return IPMPX_InitAuthentication_Tag;
	else if (!stricmp(dataName, "IPMP_TrustSecurityMetadata")) return IPMPX_TrustSecurityMetadata_Tag;
	else if (!stricmp(dataName, "IPMP_TrustedTool")) return IPMPX_TrustedTool_Tag;
	else if (!stricmp(dataName, "IPMP_TrustSpecification")) return IPMPX_TrustSpecification_Tag;
	else if (!stricmp(dataName, "IPMP_MutualAuthentication")) return IPMPX_MutualAuthentication_Tag;
	else if (!stricmp(dataName, "IPMP_AlgorithmDescriptor")) return IPMPX_AlgorithmDescr_Tag;
	else if (!stricmp(dataName, "IPMP_KeyDescriptor")) return IPMPX_KeyDescr_Tag;
	else if (!stricmp(dataName, "IPMP_GetToolsResponse")) return IPMPX_GetToolsResponse_Tag;
	else if (!stricmp(dataName, "IPMP_ParametricDescription")) return IPMPX_ParametricDescription_Tag;
	else if (!stricmp(dataName, "IPMP_ParametricDescriptionItem")) return IPMPX_ParamDescrItem_Tag;
	else if (!stricmp(dataName, "IPMP_ToolParamCapabilitiesQuery")) return IPMPX_ToolParamCapabilitiesQuery_Tag;
	else if (!stricmp(dataName, "IPMP_ToolParamCapabilitiesResponse")) return IPMPX_ToolParamCapabilitiesResponse_Tag;
	else if (!stricmp(dataName, "IPMP_ConnectTool")) return IPMPX_ConnectTool_Tag;
	else if (!stricmp(dataName, "IPMP_DisconnectTool")) return IPMPX_DisconnectTool_Tag;
	else if (!stricmp(dataName, "IPMP_GetToolContext")) return IPMPX_GetToolContext_Tag;
	else if (!stricmp(dataName, "IPMP_GetToolContextResponse")) return IPMPX_GetToolContextResponse_Tag;
	else if (!stricmp(dataName, "IPMP_AddToolNotificationListener")) return IPMPX_AddToolNotificationListener_Tag;
	else if (!stricmp(dataName, "IPMP_RemoveToolNotificationListener")) return IPMPX_RemoveToolNotificationListener_Tag;
	else if (!stricmp(dataName, "IPMP_NotifyToolEvent")) return IPMPX_NotifyToolEvent_Tag;
	else if (!stricmp(dataName, "IPMP_CanProcess")) return IPMPX_CanProcess_Tag;
	else if (!stricmp(dataName, "IPMP_ToolAPI_Config")) return IPMPX_ToolAPI_Config_Tag;
	else if (!stricmp(dataName, "IPMP_AudioWatermarkingInit")) return IPMPX_AudioWatermarkingInit_Tag;
	else if (!stricmp(dataName, "IPMP_VideoWatermarkingInit")) return IPMPX_VideoWatermarkingInit_Tag;
	else if (!stricmp(dataName, "IPMP_SendAudioWatermark")) return IPMPX_SendAudioWatermark_Tag;
	else if (!stricmp(dataName, "IPMP_SendVideoWatermark")) return IPMPX_SendVideoWatermark_Tag;
	else if (!stricmp(dataName, "IPMP_SelectiveDecryptionInit")) return IPMPX_SelectiveDecryptionInit_Tag;
	else if (!stricmp(dataName, "IPMP_SelectiveBuffer")) return IPMPX_SelEncBuffer_Tag;
	else if (!stricmp(dataName, "IPMP_SelectiveField")) return IPMPX_SelEncField_Tag;
	else if (!stricmp(dataName, "ISMACryp_Data")) return IPMPX_ISMACryp_Tag;
	return 0;
}

u32 IPMPX_GetFieldType(IPMPX_Data *p, char *fieldName)
{
	switch (p->tag) {
	case IPMPX_KeyData_Tag:
		if (!stricmp(fieldName, "keyBody")|| !stricmp(fieldName, "opaqueData")) return DFT_IPMPX_BA;
		break;
	case IPMPX_RightsData_Tag:
		if (!stricmp(fieldName, "rightsInfo")) return DFT_IPMPX_BA;
		break;
	case IPMPX_OpaqueData_Tag:
		if (!stricmp(fieldName, "OpaqueData")) return DFT_IPMPX_BA;
		break;
	case IPMPX_SecureContainer_Tag:
		if (!stricmp(fieldName, "encryptedData") || !stricmp(fieldName, "MAC")) return DFT_IPMPX_BA;
		else if (!stricmp(fieldName, "protectedMsg")) return DFT_IPMPX;
		break;
	case IPMPX_TrustSecurityMetadata_Tag:
		if (!stricmp(fieldName, "trustedTools")) return DFT_IPMPX_LIST;
		break;
	case IPMPX_TrustedTool_Tag:
		if (!stricmp(fieldName, "trustSpecifications")) return DFT_IPMPX_LIST;
		break;
	case IPMPX_TrustSpecification_Tag:
		if (!stricmp(fieldName, "CCTrustMetadata")) return DFT_IPMPX_BA;
		break;
	case IPMPX_MutualAuthentication_Tag:
		if (!stricmp(fieldName, "candidateAlgorithms") || !stricmp(fieldName, "agreedAlgorithms")) return DFT_IPMPX_LIST;
		else if (!stricmp(fieldName, "certificates")) return DFT_IPMPX_BA_LIST;
		else if (!stricmp(fieldName, "publicKey") || !stricmp(fieldName, "trustData")) return DFT_IPMPX;
		else if (!stricmp(fieldName, "authCodes") || !stricmp(fieldName, "opaque") || !stricmp(fieldName, "AuthenticationData")) 
			return DFT_IPMPX_BA;
		break;
	case IPMPX_AlgorithmDescr_Tag:
		if (!stricmp(fieldName, "specAlgoID") || !stricmp(fieldName, "OpaqueData")) return DFT_IPMPX_BA;
		break;
	case IPMPX_GetToolsResponse_Tag:
		if (!stricmp(fieldName, "ipmp_tools")) return DFT_OD_LIST;
		break;
	case IPMPX_ParametricDescription_Tag:
		if (!stricmp(fieldName, "descriptionComment")) return DFT_IPMPX_BA;
		else if (!stricmp(fieldName, "descriptions")) return DFT_IPMPX_LIST;
		break;
	case IPMPX_ParamDescrItem_Tag: 
		/*all is IPMPX data*/
		return DFT_IPMPX_BA;
	case IPMPX_ToolParamCapabilitiesQuery_Tag:
		if (!stricmp(fieldName, "description")) return DFT_IPMPX;
		break;
	case IPMPX_ConnectTool_Tag:
		if (!stricmp(fieldName, "toolDescriptor")) return DFT_OD;
		break;
	case IPMPX_ToolAPI_Config_Tag:
		if (!stricmp(fieldName, "opaqueData")) return DFT_IPMPX_BA;
		break;
	case IPMPX_SendAudioWatermark_Tag:
	case IPMPX_SendVideoWatermark_Tag:
		if (!stricmp(fieldName, "payload") || !stricmp(fieldName, "opaqueData")) return DFT_IPMPX_BA;
		break;
	case IPMPX_SelectiveDecryptionInit_Tag:
		if (!stricmp(fieldName, "SelectiveBuffers") || !stricmp(fieldName, "SelectiveFields")) return DFT_IPMPX_LIST;
		break;
	case IPMPX_SelEncBuffer_Tag:
		if (!stricmp(fieldName, "StreamCipher")) return DFT_IPMPX_BA;
		break;
	case IPMPX_SelEncField_Tag:
		if (!stricmp(fieldName, "shuffleSpecificInfo")) return DFT_IPMPX_BA;
		break;
	default:
		break;
	}
	return 0;
}

#define GET_U8(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u8) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u8) d; }	}	
#define GET_U16(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u16) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u16) d; }	}	
#define GET_U32(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u32) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u32) d; }	}	
#define GET_S32(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (s32) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (s32) d; }	}	
#define GET_BOOL(field) { ret = 1; field = (!stricmp(val, "true") || !stricmp(val, "1")) ? 1 : 0; }

#define GET_DOUBLE(field) { Float v; ret = 1; sscanf(val, "%f", &v); field = (Double) v;}
#define GET_STRING(field) { ret = 1; field = strdup(val); if (val[0] == '"') strcpy(field, val+1); if (field[strlen(field)-1] == '"') field[strlen(field)-1] = 0; }

void IPMPX_ParseBinData(char *val, char **out_data, u32 *out_data_size)
{
	u32 i, c, len;
	char s[3];

	if (val[0] != '%') {
		len = *out_data_size = strlen(val);
		*out_data = malloc(sizeof(char) * len);
		memcpy(*out_data, val, sizeof(char) * len);
		return;
	}

	len = strlen(val) / 3;
	if (*out_data) free(*out_data);
	*out_data_size = len;
	*out_data = malloc(sizeof(char) * len);
	s[2] = 0;
	for (i=0; i<len; i++) {
		s[0] = val[3*i+1];
		s[1] = val[3*i+2];
		sscanf(s, "%02X", &c);
		(*out_data)[i] = (unsigned char) c;
	}
}

void IPMPX_ParseFileData(char *fileName, char **out_data, u32 *out_data_size)
{
	FILE *f;
	u32 size;
	if (*out_data) free(*out_data);
	*out_data = NULL;
	*out_data_size = 0;
	f = fopen(fileName, "rb");
	if (!f) {
		fprintf(stdout, "WARNING: cannot open data file %s - ignoring config\n", fileName);
		return;
	}
	fseek(f, 0, SEEK_END);
	size = ftell(f);
	fseek(f, 0, SEEK_SET);
	*out_data_size = size;
	*out_data = malloc(sizeof(char) * size);
	fread(*out_data, sizeof(char) * size, 1, f);
	fclose(f);
}

void IPMPX_ParseBin128(char *val, bin128 *data)
{
	if (!strnicmp(val, "0x", 2)) val+=2;

	if (strlen(val)<16) {
		BitStream *bs;
		u32 int_val = atoi(val);
		bs = NewBitStream((unsigned char *) (*data), 16, BS_WRITE);
		BS_WriteInt(bs, 0, 32);
		BS_WriteInt(bs, 0, 32);
		BS_WriteInt(bs, 0, 32);
		BS_WriteInt(bs, int_val, 32);
		DeleteBitStream(bs);
	} else {
		u32 i, b;
		char szB[3];
		szB[2] = 0;
		for (i=0; i<16; i++) {
			szB[0] = val[2*i];
			szB[1] = val[2*i+1];
			sscanf(szB, "%x", &b);
			((char *)data)[i] = (u8) b;
		}
	}
}

void IPMPX_ParseDate(char *val, IPMPX_Date *date)
{
	if ((strlen(val)<7) || strnicmp(val, "0x", 2)) {
		BitStream *bs;
		u32 int_val = atoi(val);
		bs = NewBitStream((unsigned char *) (*date), 5, BS_WRITE);
		BS_WriteInt(bs, 0, 8);
		BS_WriteInt(bs, int_val, 32);
		DeleteBitStream(bs);
	} else {
		val += 2;
		memcpy((char *) (*date), val, sizeof(char)*5);
	}
}

M4Err IPMPX_ParseEventType(char *val, u8 *eventType, u8 *eventTypeCount)
{
	char szVal[50];
	u32 i, j, len, v;
	*eventTypeCount = 0;
	len = strlen(val);
	j=0;
	for (i=0; i<len; i++) {
		switch (val[i]) {
		case '\'': case '\"': case ',': case ' ':
			break;
		default:
			szVal[j] = val[i];
			j++;
			if (i+1==len) break;
			continue;
		}
		if (j) {
			szVal[j] = 0;
			if (!strnicmp(szVal, "0x", 2)) { sscanf(szVal, "%x", &v); eventType[*eventTypeCount] = v; }
			else { sscanf(szVal, "%d", &v); eventType[*eventTypeCount] = v; }
			j=0;
			(*eventTypeCount) += 1;
			if (*eventTypeCount == 9) return M4OK;
		}
	}
	return M4OK;
}

M4Err IPMPX_ParseData_16(char *val, u16 **outData, u16 *outDataSize)
{
	char szVal[50];
	u32 i, j, len, v, alloc, count;
	u16 *data = malloc(sizeof(u16) * 100);
	alloc = 100;

	len = strlen(val);
	count = j = 0;
	for (i=0; i<len; i++) {
		switch (val[i]) {
		case '\'': case '\"': case ',': case ' ':
			break;
		default:
			szVal[j] = val[i];
			j++;
			if (i+1==len) break;
			continue;
		}
		if (j) {
			szVal[j] = 0;
			if (!strnicmp(szVal, "0x", 2)) { sscanf(szVal, "%x", &v); data[count] = v; }
			else { sscanf(szVal, "%d", &v); data[count] = v; }
			j=0;
			count += 1;
			if (count == alloc) {
				alloc += 100;
				data = realloc(data, sizeof(u16)*alloc);
			}
		}
	}
	(*outData) = realloc(data, sizeof(u16)*count);
	*outDataSize = count;
	return M4OK;
}

M4Err IPMPX_SetField(IPMPX_Data *_p, char *fieldName, char *val)
{
	u32 d, ret = 0;

	if (!stricmp(val, "auto")) return M4OK;
	else if (!stricmp(val, "unspecified")) return M4OK;

	switch (_p->tag) {
	case IPMPX_KeyData_Tag:
	{
		IPMPX_KeyData *p = (IPMPX_KeyData*)_p;
		if (!stricmp(fieldName, "hasStartDTS")) {
			if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~1;
			else p->flags |= 1;
			ret = 1;
		}
		else if (!stricmp(fieldName, "hasStartPacketID")) {
			if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~2;
			else p->flags |= 2;
			ret = 1;
		}
		else if (!stricmp(fieldName, "hasExpireDTS")) {
			if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~4;
			else p->flags |= 4;
			ret = 1;
		}
		else if (!stricmp(fieldName, "hasExpirePacketID")) {
			if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~8;
			else p->flags |= 8;
			ret = 1;
		}
		else if (!stricmp(fieldName, "startDTS")) GET_U32(p->startDTS)
		else if (!stricmp(fieldName, "startPacketID")) GET_U32(p->startPacketID)
		else if (!stricmp(fieldName, "expireDTS")) GET_U32(p->expireDTS)
		else if (!stricmp(fieldName, "expirePacketID")) GET_U32(p->expirePacketID)
	}
		break;
	case IPMPX_SecureContainer_Tag:
	{
		IPMPX_SecureContainer*p = (IPMPX_SecureContainer*)_p;
		if (!stricmp(fieldName, "isMACEncrypted")) GET_BOOL(p->isMACEncrypted)
	}
		break;
	case IPMPX_InitAuthentication_Tag:
	{
		IPMPX_InitAuthentication *p = (IPMPX_InitAuthentication*)_p;
		if (!stricmp(fieldName, "Context")) GET_U32(p->Context)
		else if (!stricmp(fieldName, "AuthType")) GET_U8(p->AuthType)
	}
		break;
	case IPMPX_TrustedTool_Tag:
	{
		IPMPX_TrustedTool *p = (IPMPX_TrustedTool*)_p;
		if (!stricmp(fieldName, "toolID")) { IPMPX_ParseBin128(val, &p->toolID); ret = 1; }
		else if (!stricmp(fieldName, "AuditDate")) { IPMPX_ParseDate(val, &p->AuditDate); ret = 1; }
	}
		break;
	case IPMPX_TrustSpecification_Tag:
	{
		IPMPX_TrustSpecification *p = (IPMPX_TrustSpecification*)_p;
		if (!stricmp(fieldName, "startDate")) { IPMPX_ParseDate(val, &p->startDate); ret = 1; }
		else if (!stricmp(fieldName, "attackerProfile")) GET_U8(p->attackerProfile)
		else if (!stricmp(fieldName, "trustedDuration")) GET_U32(p->trustedDuration)
	}
		break;
	case IPMPX_AlgorithmDescr_Tag:
	{
		IPMPX_AUTH_AlgorithmDescriptor *p = (IPMPX_AUTH_AlgorithmDescriptor *)_p;
		if (!stricmp(fieldName, "regAlgoID")) GET_U16(p->regAlgoID)
	}
		break;
	case IPMPX_KeyDescr_Tag:
	{
		IPMPX_AUTH_KeyDescriptor *p = (IPMPX_AUTH_KeyDescriptor *)_p;
		if (!stricmp(fieldName, "keyBody")) {
			u32 s;
			IPMPX_ParseBinData(val, &p->keyBody, &s);
			p->keyBodyLength = s; 
			ret = 1;
		}
	}
		break;
	case IPMPX_ParametricDescription_Tag:
	{
		IPMPX_ParametricDescription*p = (IPMPX_ParametricDescription*)_p;
		if (!stricmp(fieldName, "majorVersion")) GET_U8(p->majorVersion)
		else if (!stricmp(fieldName, "minorVersion")) GET_U8(p->minorVersion)
	}
		break;
	case IPMPX_ToolParamCapabilitiesResponse_Tag:
	{
		IPMPX_ToolParamCapabilitiesResponse*p = (IPMPX_ToolParamCapabilitiesResponse*)_p;
		if (!stricmp(fieldName, "capabilitiesSupported")) GET_BOOL(p->capabilitiesSupported)
	}
		break;
	case IPMPX_DisconnectTool_Tag:
	{
		IPMPX_DisconnectTool*p = (IPMPX_DisconnectTool*)_p;
		if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID)
	}
		break;
	case IPMPX_GetToolContext_Tag:
	{
		IPMPX_GetToolContext*p = (IPMPX_GetToolContext*)_p;
		if (!stricmp(fieldName, "scope")) GET_U8(p->scope)
		else if (!stricmp(fieldName, "IPMP_DescriptorIDEx")) GET_U16(p->IPMP_DescriptorIDEx)
	}
		break;
	case IPMPX_GetToolContextResponse_Tag:
	{
		IPMPX_GetToolContextResponse*p = (IPMPX_GetToolContextResponse*)_p;
		if (!stricmp(fieldName, "OD_ID")) GET_U16(p->OD_ID)
		else if (!stricmp(fieldName, "ESD_ID")) GET_U16(p->ESD_ID)
		else if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID)
	}
		break;
	case IPMPX_AddToolNotificationListener_Tag:
	{
		IPMPX_AddToolNotificationListener*p = (IPMPX_AddToolNotificationListener*)_p;
		if (!stricmp(fieldName, "eventType")) return IPMPX_ParseEventType(val, p->eventType, &p->eventTypeCount);
		else if (!stricmp(fieldName, "scope")) GET_U8(p->scope)
	}
		break;
	case IPMPX_RemoveToolNotificationListener_Tag:
	{
		IPMPX_RemoveToolNotificationListener*p = (IPMPX_RemoveToolNotificationListener*)_p;
		if (!stricmp(fieldName, "eventType")) return IPMPX_ParseEventType(val, p->eventType, &p->eventTypeCount);
	}
		break;
	case IPMPX_NotifyToolEvent_Tag:
	{
		IPMPX_NotifyToolEvent*p = (IPMPX_NotifyToolEvent*)_p;
		if (!stricmp(fieldName, "OD_ID")) GET_U16(p->OD_ID)
		else if (!stricmp(fieldName, "ESD_ID")) GET_U16(p->ESD_ID)
		else if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID)
		else if (!stricmp(fieldName, "eventType")) GET_U8(p->eventType)
	}
		break;
	case IPMPX_CanProcess_Tag:
	{
		IPMPX_CanProcess*p = (IPMPX_CanProcess*)_p;
		if (!stricmp(fieldName, "canProcess")) GET_BOOL(p->canProcess)
	}
		break;
	case IPMPX_ToolAPI_Config_Tag:
	{
		IPMPX_ToolAPI_Config*p = (IPMPX_ToolAPI_Config*)_p;
		if (!stricmp(fieldName, "Instantiation_API_ID")) GET_U32(p->Instantiation_API_ID)
		else if (!stricmp(fieldName, "Messaging_API_ID")) GET_U32(p->Messaging_API_ID)
	}
		break;
	case IPMPX_AudioWatermarkingInit_Tag:
	case IPMPX_VideoWatermarkingInit_Tag:
	{
		IPMPX_WatermarkingInit *p = (IPMPX_WatermarkingInit*)_p;
		if (!stricmp(fieldName, "inputFormat")) GET_U8(p->inputFormat)
		else if (!stricmp(fieldName, "requiredOp")) GET_U8(p->requiredOp)
		else if (!stricmp(fieldName, "nChannels")) GET_U8(p->nChannels)
		else if (!stricmp(fieldName, "bitPerSample")) GET_U8(p->bitPerSample)
		else if (!stricmp(fieldName, "frequency")) GET_U32(p->frequency)
		else if (!stricmp(fieldName, "frame_horizontal_size")) GET_U16(p->frame_horizontal_size)
		else if (!stricmp(fieldName, "frame_vertical_size")) GET_U16(p->frame_vertical_size)
		else if (!stricmp(fieldName, "chroma_format")) GET_U8(p->chroma_format)
		else if (!stricmp(fieldName, "wmPayload")) { ret=1; IPMPX_ParseBinData(val, &p->wmPayload, &p->wmPayloadLen); }
		else if (!stricmp(fieldName, "opaqueData")) { ret=1; IPMPX_ParseBinData(val, &p->opaqueData, &p->opaqueDataSize); }
		else if (!stricmp(fieldName, "wmRecipientId")) GET_U16(p->wmRecipientId)
	}
		break;
	case IPMPX_SendAudioWatermark_Tag:
	case IPMPX_SendVideoWatermark_Tag:
	{
		IPMPX_SendWatermark *p = (IPMPX_SendWatermark*)_p;
		if (!stricmp(fieldName, "wm_status")) GET_U8(p->wm_status)
		else if (!stricmp(fieldName, "compression_status")) GET_U8(p->compression_status)
	}
		break;
	case IPMPX_SelectiveDecryptionInit_Tag:
	{
		IPMPX_SelectiveDecryptionInit*p = (IPMPX_SelectiveDecryptionInit*)_p;
		if (!stricmp(fieldName, "mediaTypeExtension")) GET_U8(p->mediaTypeExtension)
		else if (!stricmp(fieldName, "mediaTypeExtension")) GET_U8(p->mediaTypeExtension)
		else if (!stricmp(fieldName, "mediaTypeIndication")) GET_U8(p->mediaTypeIndication)
		else if (!stricmp(fieldName, "profileLevelIndication")) GET_U8(p->profileLevelIndication)
		else if (!stricmp(fieldName, "compliance")) GET_U8(p->compliance)
		else if (!stricmp(fieldName, "RLE_Data")) { ret=1; IPMPX_ParseData_16(val, &p->RLE_Data, &p->RLE_DataLength); }
	}
		break;
	case IPMPX_SelEncBuffer_Tag:
	{
		IPMPX_SelEncBuffer*p = (IPMPX_SelEncBuffer*)_p;
		if (!stricmp(fieldName, "cipher_Id")) { IPMPX_ParseBin128(val, &p->cipher_Id); ret = 1; }
		else if (!stricmp(fieldName, "syncBoundary")) GET_U8(p->syncBoundary)
		else if (!stricmp(fieldName, "mode")) GET_U8(p->mode)
		else if (!stricmp(fieldName, "blockSize")) GET_U16(p->blockSize)
		else if (!stricmp(fieldName, "keySize")) GET_U16(p->keySize)
	}
		break;
	case IPMPX_SelEncField_Tag:
	{
		IPMPX_SelEncField*p = (IPMPX_SelEncField*)_p;
		if (!stricmp(fieldName, "field_Id")) GET_U8(p->field_Id)
		else if (!stricmp(fieldName, "field_Scope")) GET_U8(p->field_Scope)
		else if (!stricmp(fieldName, "buf")) GET_U8(p->buf)
		else if (!stricmp(fieldName, "mappingTable")) { ret=1; IPMPX_ParseData_16(val, &p->mappingTable, &p->mappingTableSize); }
	}
		break;
	case IPMPX_ISMACryp_Tag:
	{
		IPMPX_ISMACryp*p = (IPMPX_ISMACryp*)_p;
		if (!stricmp(fieldName, "crypto_suite")) GET_U8(p->cryptoSuite)
		else if (!stricmp(fieldName, "IV_length")) GET_U8(p->IV_length)
		else if (!stricmp(fieldName, "selective_encryption")) GET_BOOL(p->use_selective_encryption)
		else if (!stricmp(fieldName, "key_indicator_length")) GET_U8(p->key_indicator_length)
	}
		break;
	}
	return ret ? M4OK : M4BadParam;
}

M4Err IPMPX_SetSubData(IPMPX_Data *_p, char *fieldName, IPMPX_Data *sp)
{
	switch (_p->tag) {
	case IPMPX_SecureContainer_Tag:
	{
		IPMPX_SecureContainer*p = (IPMPX_SecureContainer*)_p;
		if (p->protectedMsg) IPMPX_DeleteData(p->protectedMsg);
		p->protectedMsg = sp;
		return M4OK;
	}
		break;
	case IPMPX_TrustSecurityMetadata_Tag:
	{
		IPMPX_TrustSecurityMetadata *p = (IPMPX_TrustSecurityMetadata*)_p;
		if (!sp || (sp->tag!=IPMPX_TrustedTool_Tag) ) return M4BadParam;
		ChainAddEntry(p->TrustedTools, sp);
		return M4OK;
	}
		break;
	case IPMPX_TrustedTool_Tag:
	{
		IPMPX_TrustedTool *p = (IPMPX_TrustedTool *)_p;
		if (!sp || (sp->tag!=IPMPX_TrustSpecification_Tag) ) return M4BadParam;
		ChainAddEntry(p->trustSpecifications, sp);
		return M4OK;
	}
		break;
	case IPMPX_MutualAuthentication_Tag:
	{
		IPMPX_MutualAuthentication *p = (IPMPX_MutualAuthentication *)_p;
		if (!sp) return M4BadParam;
		switch (sp->tag) {
		case IPMPX_AlgorithmDescr_Tag:
			sp->tag = IPMPX_AUTH_AlgorithmDescr_Tag;
			if (!stricmp(fieldName, "candidateAlgorithms")) return ChainAddEntry(p->agreedAlgorithms, sp);
			else if (!stricmp(fieldName, "agreedAlgorithms")) return ChainAddEntry(p->agreedAlgorithms, sp);
			else return M4BadParam;
		case IPMPX_KeyDescr_Tag:
			sp->tag = IPMPX_AUTH_KeyDescr_Tag;
			if (!stricmp(fieldName, "candidateAlgorithms")) return ChainAddEntry(p->agreedAlgorithms, sp);
			else if (!stricmp(fieldName, "agreedAlgorithms")) return ChainAddEntry(p->agreedAlgorithms, sp);
			else if (!stricmp(fieldName, "publicKey")) {
				if (p->publicKey) IPMPX_AUTH_Delete((IPMPX_Authentication *) p->publicKey);
				p->publicKey = (IPMPX_AUTH_KeyDescriptor *) sp;
				return M4OK;
			}
			else return M4BadParam;
		case IPMPX_TrustSecurityMetadata_Tag:
			if (p->trustData) IPMPX_DeleteData((IPMPX_Data *)p->trustData);
			p->trustData = (IPMPX_TrustSecurityMetadata*)sp;
			return M4OK;
		default:
			return M4BadParam;
		}
	}
		break;
	case IPMPX_ParametricDescription_Tag:
	{
		IPMPX_ParametricDescription*p = (IPMPX_ParametricDescription*)_p;
		if (!sp || (sp->tag!=IPMPX_ParamDescrItem_Tag) ) return M4BadParam;
		if (!stricmp(fieldName, "descriptions")) return ChainAddEntry(p->descriptions, sp);
		else return M4BadParam;
	}
		break;
	case IPMPX_ToolParamCapabilitiesQuery_Tag:
	{
		IPMPX_ToolParamCapabilitiesQuery*p = (IPMPX_ToolParamCapabilitiesQuery*)_p;
		if (!sp || (sp->tag!=IPMPX_ParametricDescription_Tag) ) return M4BadParam;
		if (stricmp(fieldName, "description")) return M4BadParam;
		if (p->description) IPMPX_DeleteData((IPMPX_Data *)p->description);
		p->description = (IPMPX_ParametricDescription*)sp;
		return M4OK;
	}
		break;
	case IPMPX_SelectiveDecryptionInit_Tag:
	{
		IPMPX_SelectiveDecryptionInit*p = (IPMPX_SelectiveDecryptionInit*)_p;
		if (!sp) return M4BadParam;
		if (sp->tag==IPMPX_SelEncBuffer_Tag) return ChainAddEntry(p->SelEncBuffer, sp);
		else if (sp->tag==IPMPX_SelEncField_Tag) return ChainAddEntry(p->SelEncFields, sp);
		else return M4BadParam;
	}
		break;
	}
	return M4BadParam;
}

M4Err IPMPX_SetByteArray(IPMPX_Data *p, char *field, char *str)
{
	IPMPX_ByteArray *d;
	IPMPX_ByteArray **dest;
	d = malloc(sizeof(IPMPX_ByteArray));
	d->length = strlen(str);
	d->data = malloc(sizeof(char)*d->length);
	memcpy(d->data, str, d->length);

	dest = NULL;
	switch (p->tag) {
	case IPMPX_KeyData_Tag:
		if (!stricmp(field, "keyBody")) dest = & ((IPMPX_KeyData*)p)->keyBody;
		else if (!stricmp(field, "opaqueData")) dest = & ((IPMPX_KeyData*)p)->OpaqueData;
		break;
	case IPMPX_RightsData_Tag:
		if (!stricmp(field, "rightsInfo")) dest = & ((IPMPX_RightsData*)p)->rightsInfo;
		break;
	case IPMPX_OpaqueData_Tag:
		if (!stricmp(field, "opaqueData")) dest = & ((IPMPX_OpaqueData*)p)->opaqueData;
		break;
	case IPMPX_SecureContainer_Tag:
		if (!stricmp(field, "encryptedData")) dest = & ((IPMPX_SecureContainer*)p)->encryptedData;
		else if (!stricmp(field, "MAC")) dest = & ((IPMPX_SecureContainer*)p)->MAC;
		break;
	case IPMPX_TrustSpecification_Tag:
		if (!stricmp(field, "CCTrustMetadata")) dest = & ((IPMPX_TrustSpecification*)p)->CCTrustMetadata;
		break;
	case IPMPX_MutualAuthentication_Tag:
		if (!stricmp(field, "AuthenticationData")) dest = & ((IPMPX_MutualAuthentication*)p)->AuthenticationData;
		else if (!stricmp(field, "opaque")) dest = & ((IPMPX_MutualAuthentication*)p)->opaque;
		else if (!stricmp(field, "authCodes")) dest = & ((IPMPX_MutualAuthentication*)p)->authCodes;
		else if (!stricmp(field, "certificates")) {
			ChainAddEntry(((IPMPX_MutualAuthentication*)p)->certificates, d);
			return M4OK;
		}
		break;
	case IPMPX_ParametricDescription_Tag:
		if (!stricmp(field, "descriptionComment")) dest = & ((IPMPX_ParametricDescription*)p)->descriptionComment;
		break;
	case IPMPX_ParamDescrItem_Tag:
		if (!stricmp(field, "class")) dest = & ((IPMPX_ParametricDescriptionItem*)p)->main_class;
		else if (!stricmp(field, "subClass")) dest = & ((IPMPX_ParametricDescriptionItem*)p)->subClass;
		else if (!stricmp(field, "typeData")) dest = & ((IPMPX_ParametricDescriptionItem*)p)->typeData;
		else if (!stricmp(field, "type")) dest = & ((IPMPX_ParametricDescriptionItem*)p)->type;
		else if (!stricmp(field, "addedData")) dest = & ((IPMPX_ParametricDescriptionItem*)p)->addedData;
		break;
	case IPMPX_ToolAPI_Config_Tag:
		if (!stricmp(field, "opaqueData")) dest = & ((IPMPX_ToolAPI_Config*)p)->opaqueData;
		break;
	case IPMPX_SendAudioWatermark_Tag:
	case IPMPX_SendVideoWatermark_Tag:
		if (!stricmp(field, "payload")) dest = & ((IPMPX_SendWatermark *)p)->payload;
		else if (!stricmp(field, "opaqueData")) dest = & ((IPMPX_SendWatermark*)p)->opaqueData;
		break;
	case IPMPX_SelEncBuffer_Tag:
		if (!stricmp(field, "StreamCipher")) dest = & ((IPMPX_SelEncBuffer*)p)->Stream_Cipher_Specific_Init_Info;
		break;
	case IPMPX_SelEncField_Tag:
		if (!stricmp(field, "shuffleSpecificInfo")) dest = & ((IPMPX_SelEncField*)p)->shuffleSpecificInfo;
		break;
	}
	if (!dest) {
		free(d->data);
		free(d);
		return M4BadParam;
	}
	if ( (*dest) ) {
		if ((*dest)->data) free((*dest)->data);
		free((*dest));
	}
	(*dest) = d;
	return M4OK;
}
