1/*
2 * UserData.c --
3 *
4 *		Implements the 'userdata' command.
5 *		It is part of the QuickTimeTcl package which provides Tcl/Tk bindings for QuickTime.
6 *
7 * Copyright (c) 2003  Mats Bengtsson
8 *
9 * $Id: UserData.c,v 1.2 2003/11/28 13:38:42 matben Exp $
10 */
11
12#include "MoviePlayer.h"
13
14const UInt8 			kUserDataIsText = 0xA9; // the copyright symbol
15
16/*
17 * Use hash table to map between human readable option names and long types
18 * for user data.
19 */
20
21static Tcl_HashTable 	*gOptionToUserDataHashPtr = NULL;
22static Tcl_HashTable 	*gUserDataToOptionHashPtr = NULL;
23
24typedef struct UserDataLookupEntry {
25    char       	*option;
26  	OSType 		udType;		/* long */
27} UserDataLookupEntry;
28
29UserDataLookupEntry gLookupTable[] = {
30	{"-album",				kUserDataTextAlbum},
31	{"-artist",				kUserDataTextArtist},
32	{"-author",				kUserDataTextAuthor},
33	{"-chapter",			kUserDataTextChapter},
34	{"-comment",			kUserDataTextComment},
35	{"-composer",			kUserDataTextComposer},
36	{"-copyright",			kUserDataTextCopyright},
37	{"-creationdate",		kUserDataTextCreationDate},
38	{"-description",		kUserDataTextDescription},
39	{"-director",			kUserDataTextDirector},
40	{"-disclaimer",			kUserDataTextDisclaimer},
41	{"-encodedby",			kUserDataTextEncodedBy},
42	{"-fullname",			kUserDataTextFullName},
43	{"-genre",				kUserDataTextGenre},
44	{"-hostcomputer",		kUserDataTextHostComputer},
45	{"-information",		kUserDataTextInformation},
46	{"-keywords",			kUserDataTextKeywords},
47	{"-make",				kUserDataTextMake},
48	{"-model",				kUserDataTextModel},
49	{"-name",				kUserDataName},
50	{"-originalartist",		kUserDataTextOriginalArtist},
51	{"-originalformat",		kUserDataTextOriginalFormat},
52	{"-originalsource",		kUserDataTextOriginalSource},
53	{"-performers",			kUserDataTextPerformers},
54	{"-producer",			kUserDataTextProducer},
55	{"-product",			kUserDataTextProduct},
56	{"-software",			kUserDataTextSoftware},
57	{"-specialplaybackrequirements",	kUserDataTextSpecialPlaybackRequirements},
58	{"-track",				kUserDataTextTrack},
59	{"-warning",			kUserDataTextWarning},
60	{"-writer",				kUserDataTextWriter},
61	{"-urllink",			kUserDataTextURLLink},
62	{"-editdate1",			kUserDataTextEditDate1},
63	{NULL, 0}
64};
65
66static int			SetupUserDataOptionHashTables( Tcl_Interp *interp );
67
68
69/*
70 *----------------------------------------------------------------------
71 *
72 * GetUserDataListCmd --
73 *
74 *		Process the "userdata" subcommand.
75 *
76 * Results:
77 *  	Normal TCL results
78 *
79 * Side effects:
80 *		None.
81 *
82 *----------------------------------------------------------------------
83 */
84
85int
86GetUserDataListCmd( UserData userData,
87		Tcl_Interp *interp )
88{
89	Tcl_Obj         *listObjPtr;
90  	OSType 			udType;
91  	short 			count, i;
92  	char 			nul = 0;
93  	char			str8[8];
94  	char			*charPtr;
95	unsigned long	lType;
96  	Handle 			hData = NULL;
97	Tcl_HashEntry   *hashPtr = NULL;
98  	Ptr 			p;
99	Tcl_DString     ds;
100  	OSErr 			err = noErr;
101    int 			result = TCL_OK;
102
103  	if (gOptionToUserDataHashPtr == NULL) {
104  		if (TCL_OK != SetupUserDataOptionHashTables( interp )) {
105  			result = TCL_ERROR;
106  			goto bail;
107  		}
108  	}
109	listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
110  	hData = NewHandle(0);
111  	udType = GetNextUserDataType( userData, 0 );
112
113  	if(0 != udType) {
114    	do {
115      		count = CountUserDataType( userData, udType );
116      		for(i = 1; i <= count; i++) {
117	        	if(((udType>>24) == kUserDataIsText) || (udType == kUserDataName)) {
118
119	          		/*
120	          		 * If the first letter of udType is 0xA9
121	          		 * (the copyright symbol), then use GetUserDataText instead
122	          		 * of GetUserData. See Movies.h for a list of interesting
123	          		 * user data types.
124	          		 */
125
126	          		err = GetUserDataText( userData, hData, udType, i, langEnglish );
127	          		if (err != noErr) {
128				 		CheckAndSetErrorResult( interp, err );
129	          			goto bail;
130	          		}
131
132	          		/* null-terminate the string in the handle */
133	          		PtrAndHand( &nul, hData, 1 );
134
135	          		/* turn any CRs into spaces */
136	          		p = *hData;
137	          		while (*p) {
138	            		if (*p == kReturnCharCode) {
139	            			*p = ' ';
140	            		}
141	            		p++;
142	          		};
143
144	          		/*
145	          		 * Find any option string in hash table for this ud type.
146	          		 */
147
148				    hashPtr = Tcl_FindHashEntry( gUserDataToOptionHashPtr,
149				    		(char *) udType );
150				    if (hashPtr != NULL) {
151				        charPtr = (char *) Tcl_GetHashValue( hashPtr );
152						Tcl_ListObjAppendElement( interp, listObjPtr,
153								Tcl_NewStringObj(charPtr, -1) );
154				    } else {
155						lType = EndianU32_BtoN( udType );
156						memset( str8, 0, 8 );
157						strcat( str8, "-" );
158						memcpy( str8 + 1, &lType, 4 );
159						Tcl_ListObjAppendElement( interp, listObjPtr,
160								Tcl_NewStringObj(str8, -1) );
161				    }
162
163	         		HLock( hData );
164                    charPtr = Tcl_ExternalToUtfDString( gQTTclTranslationEncoding, *hData, -1, &ds );
165    				Tcl_ListObjAppendElement( interp, listObjPtr,
166        					Tcl_NewStringObj(charPtr, Tcl_DStringLength(&ds)) );
167	          		HUnlock( hData );
168	        	} else {
169
170					/* Non text user data. */
171	          		err = GetUserData( userData, hData, udType, i );
172	          		if (err != noErr) {
173				 		CheckAndSetErrorResult( interp, err );
174				 		result = TCL_ERROR;
175	          			goto bail;
176	          		}
177
178					lType = EndianU32_BtoN( udType );
179					memset( str8, 0, 8 );
180					strcat( str8, "-" );
181					memcpy( str8 + 1, &lType, 4 );
182					Tcl_ListObjAppendElement( interp, listObjPtr,
183							Tcl_NewStringObj(str8, -1) );
184	         		HLock( hData );
185    				Tcl_ListObjAppendElement( interp, listObjPtr,
186        					Tcl_NewStringObj( *hData, GetHandleSize(hData) ) );
187	          		HUnlock( hData );
188	        	}
189	   		}
190	      	udType = GetNextUserDataType( userData, udType );
191
192 		} while (0 != udType);
193  	}
194
195    Tcl_SetObjResult( interp, listObjPtr );
196
197bail:
198	if (hData != NULL) {
199		DisposeHandle( hData );
200	}
201	return result;
202}
203
204/*
205 *----------------------------------------------------------------------
206 *
207 * SetUserDataListCmd --
208 *
209 *		Process the "userdata" subcommand.
210 *
211 * Results:
212 *  	Normal TCL results
213 *
214 * Side effects:
215 *		Depends on the command.
216 *
217 *----------------------------------------------------------------------
218 */
219
220int
221SetUserDataListCmd( UserData userData,
222		Tcl_Interp *interp,
223		int objc,
224		Tcl_Obj *CONST objv[] )
225{
226	int				iarg;
227  	OSType 			udType;
228  	Handle 			hData = NULL;
229  	char			*charPtr;
230  	int				isTextUserData;
231  	int				len;
232	unsigned long	lType;
233	Tcl_DString     ds;
234	Tcl_HashEntry   *hashPtr = NULL;
235  	OSErr 			err = noErr;
236    int 			result = TCL_OK;
237
238  	if (gOptionToUserDataHashPtr == NULL) {
239  		if (SetupUserDataOptionHashTables( interp ) != TCL_OK) {
240  			result = TCL_ERROR;
241  			goto bail;
242  		}
243  	}
244
245    for (iarg = 0; iarg < objc; iarg += 2) {
246	    hashPtr = Tcl_FindHashEntry( gOptionToUserDataHashPtr,
247	    		Tcl_GetString( objv[iarg] ) );
248	    if (hashPtr != NULL) {
249			udType = (OSType) Tcl_GetHashValue( hashPtr );
250			isTextUserData = true;
251		} else {
252
253			/*
254			 * The argument did not match any of the predefined options.
255			 * UTF translation first!
256			 */
257
258	        charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding,
259	        		Tcl_GetString( objv[iarg] ), -1, &ds );
260        	if ((charPtr[0] != '-') || (Tcl_DStringLength(&ds) != 5)) {
261        		Tcl_SetObjResult( interp, Tcl_NewStringObj(
262        				"userdata type option must be -FOUR if not predefined types", -1 ) );
263	  			result = TCL_ERROR;
264	  			goto bail;
265			}
266			memcpy( &lType, charPtr+1, 4 );
267			udType = EndianU32_NtoB( lType );
268	        if((udType>>24) == kUserDataIsText) {
269				isTextUserData = true;
270			} else {
271				isTextUserData = false;
272			}
273        }
274
275        /* Get rid of any old before setting the new one. */
276        if (isTextUserData) {
277        	while (RemoveUserDataText( userData, udType, 1, langEnglish ) == noErr)
278            	;
279	        charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding,
280	        		Tcl_GetString( objv[iarg+1] ), -1, &ds );
281	        len = Tcl_DStringLength(&ds);
282	        PtrToHand( charPtr, &hData, len + 1 );
283		    HLock( hData );
284	        err = AddUserDataText( userData, hData, udType, 1, langEnglish );
285		 	HUnlock( hData );
286		 	Tcl_DStringFree( &ds );
287        } else {
288        	while (RemoveUserData( userData, udType, 1 ) == noErr)
289            	;
290            charPtr = Tcl_GetStringFromObj( objv[iarg+1], &len );
291       		err = SetUserDataItem( userData, charPtr, len, udType, 1 );
292        }
293   	 	if (err != noErr) {
294	 		CheckAndSetErrorResult( interp, err );
295			result = TCL_ERROR;
296	  		goto bail;
297		}
298	}
299
300bail:
301
302	return result;
303}
304
305/*
306 *----------------------------------------------------------------------
307 *
308 * SetupUserDataOptionHashTables --
309 *
310 *		Sets up two hash tables from User Data types to option strings.
311 *
312 * Results:
313 *  	Normal TCL results
314 *
315 * Side effects:
316 *		hash tables made.
317 *
318 *----------------------------------------------------------------------
319 */
320
321static int
322SetupUserDataOptionHashTables( Tcl_Interp *interp )
323{
324	Tcl_HashEntry 		*hPtr;
325	UserDataLookupEntry	*entryPtr;
326	int					isNew;
327    int 				result = TCL_OK;
328
329    /*
330     * Map from option string to User Data Type (long).
331     */
332
333    gOptionToUserDataHashPtr = (Tcl_HashTable *) ckalloc( sizeof(Tcl_HashTable) );
334	Tcl_InitHashTable( gOptionToUserDataHashPtr, TCL_STRING_KEYS );
335	for (entryPtr = &gLookupTable[0]; entryPtr->option != NULL; entryPtr++) {
336		hPtr = Tcl_CreateHashEntry( gOptionToUserDataHashPtr,
337				entryPtr->option, &isNew );
338		Tcl_SetHashValue( hPtr, entryPtr->udType );
339	}
340
341    /*
342     * Map from User Data Type (long) to option string.
343     */
344
345    gUserDataToOptionHashPtr = (Tcl_HashTable *) ckalloc( sizeof(Tcl_HashTable) );
346	Tcl_InitHashTable( gUserDataToOptionHashPtr, TCL_ONE_WORD_KEYS );
347	for (entryPtr = &gLookupTable[0]; entryPtr->option != NULL; entryPtr++) {
348		hPtr = Tcl_CreateHashEntry( gUserDataToOptionHashPtr,
349				(char *) entryPtr->udType, &isNew );
350		Tcl_SetHashValue( hPtr, entryPtr->option );
351	}
352	return result;
353}
354
355void
356UserDataHashTablesFree( void )
357{
358	if (gOptionToUserDataHashPtr != NULL) {
359		Tcl_DeleteHashTable( gOptionToUserDataHashPtr );
360		ckfree( (char *) gOptionToUserDataHashPtr );
361	}
362	if (gUserDataToOptionHashPtr != NULL) {
363		Tcl_DeleteHashTable( gUserDataToOptionHashPtr );
364		ckfree( (char *) gUserDataToOptionHashPtr );
365	}
366}
367
368/*---------------------------------------------------------------------------*/