1 /*
2  * Copyright (C) 2004,2005 Charles Schmidt <cschmidt2@emich.edu>
3  * Copyright (C) 2006 INDT
4  *  Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA*
19  */
20 
21 #include "dmap-structure.h"
22 #include "dmap-private-utils.h"
23 
24 #include <glib.h>
25 #include <glib-object.h>
26 #include <gobject/gvaluecollector.h>
27 
28 #include <string.h>
29 #include <stdarg.h>
30 
31 #define MAKE_CONTENT_CODE(ch0, ch1, ch2, ch3) \
32     (( (gint32)(gchar)(ch0) | ( (gint32)(gchar)(ch1) << 8 ) | \
33     ( (gint32)(gchar)(ch2) << 16 ) | \
34     ( (gint32)(gchar)(ch3) << 24 ) ))
35 
36 static const DMAPContentCodeDefinition cc_defs[] = {
37 	{DMAP_RAW, 0, "", "", DMAP_TYPE_STRING},
38 	{DMAP_CC_MDCL, MAKE_CONTENT_CODE ('m', 'd', 'c', 'l'),
39 	 "dmap.dictionary", "mdcl", DMAP_TYPE_CONTAINER},
40 	{DMAP_CC_MSTT, MAKE_CONTENT_CODE ('m', 's', 't', 't'), "dmap.status",
41 	 "mstt", DMAP_TYPE_INT},
42 	{DMAP_CC_MIID, MAKE_CONTENT_CODE ('m', 'i', 'i', 'd'), "dmap.itemid",
43 	 "miid", DMAP_TYPE_INT},
44 	{DMAP_CC_MINM, MAKE_CONTENT_CODE ('m', 'i', 'n', 'm'),
45 	 "dmap.itemname", "minm", DMAP_TYPE_STRING},
46 	{DMAP_CC_MIKD, MAKE_CONTENT_CODE ('m', 'i', 'k', 'd'),
47 	 "dmap.itemkind", "mikd", DMAP_TYPE_BYTE},
48 	{DMAP_CC_MPER, MAKE_CONTENT_CODE ('m', 'p', 'e', 'r'),
49 	 "dmap.persistentid", "mper", DMAP_TYPE_INT64},
50 	{DMAP_CC_MCON, MAKE_CONTENT_CODE ('m', 'c', 'o', 'n'),
51 	 "dmap.container", "mcon", DMAP_TYPE_CONTAINER},
52 	{DMAP_CC_MCTI, MAKE_CONTENT_CODE ('m', 'c', 't', 'i'),
53 	 "dmap.containeritemid", "mcti", DMAP_TYPE_INT},
54 	{DMAP_CC_MPCO, MAKE_CONTENT_CODE ('m', 'p', 'c', 'o'),
55 	 "dmap.parentcontainerid", "mpco", DMAP_TYPE_INT},
56 	{DMAP_CC_MSTS, MAKE_CONTENT_CODE ('m', 's', 't', 's'),
57 	 "dmap.statusstring", "msts", DMAP_TYPE_STRING},
58 	{DMAP_CC_MIMC, MAKE_CONTENT_CODE ('m', 'i', 'm', 'c'),
59 	 "dmap.itemcount", "mimc", DMAP_TYPE_INT},
60 	{DMAP_CC_MCTC, MAKE_CONTENT_CODE ('m', 'c', 't', 'c'),
61 	 "dmap.containercount", "mctc", DMAP_TYPE_INT},
62 	{DMAP_CC_MRCO, MAKE_CONTENT_CODE ('m', 'r', 'c', 'o'),
63 	 "dmap.returnedcount", "mrco", DMAP_TYPE_INT},
64 	{DMAP_CC_MTCO, MAKE_CONTENT_CODE ('m', 't', 'c', 'o'),
65 	 "dmap.specifiedtotalcount", "mtco", DMAP_TYPE_INT},
66 	{DMAP_CC_MLCL, MAKE_CONTENT_CODE ('m', 'l', 'c', 'l'), "dmap.listing",
67 	 "mlcl", DMAP_TYPE_CONTAINER},
68 	{DMAP_CC_MLIT, MAKE_CONTENT_CODE ('m', 'l', 'i', 't'),
69 	 "dmap.listingitem", "mlit", DMAP_TYPE_CONTAINER},
70 	{DMAP_CC_MBCL, MAKE_CONTENT_CODE ('m', 'b', 'c', 'l'), "dmap.bag",
71 	 "mbcl", DMAP_TYPE_CONTAINER},
72 	{DMAP_CC_MSRV, MAKE_CONTENT_CODE ('m', 's', 'r', 'v'),
73 	 "dmap.serverinforesponse", "msrv", DMAP_TYPE_CONTAINER},
74 	{DMAP_CC_MSAU, MAKE_CONTENT_CODE ('m', 's', 'a', 'u'),
75 	 "dmap.authenticationmethod", "msau", DMAP_TYPE_BYTE},
76 	{DMAP_CC_MSLR, MAKE_CONTENT_CODE ('m', 's', 'l', 'r'),
77 	 "dmap.loginrequired", "mslr", DMAP_TYPE_BYTE},
78 	{DMAP_CC_MPRO, MAKE_CONTENT_CODE ('m', 'p', 'r', 'o'),
79 	 "dmap.protocolversion", "mpro", DMAP_TYPE_VERSION},
80 	{DMAP_CC_MSAL, MAKE_CONTENT_CODE ('m', 's', 'a', 'l'),
81 	 "dmap.supportsautologout", "msal", DMAP_TYPE_BYTE},
82 	{DMAP_CC_MSUP, MAKE_CONTENT_CODE ('m', 's', 'u', 'p'),
83 	 "dmap.supportsupdate", "msup", DMAP_TYPE_BYTE},
84 	{DMAP_CC_MSPI, MAKE_CONTENT_CODE ('m', 's', 'p', 'i'),
85 	 "dmap.supportspersistenids", "mspi", DMAP_TYPE_BYTE},
86 	{DMAP_CC_MSEX, MAKE_CONTENT_CODE ('m', 's', 'e', 'x'),
87 	 "dmap.supportsextensions", "msex", DMAP_TYPE_BYTE},
88 	{DMAP_CC_MSBR, MAKE_CONTENT_CODE ('m', 's', 'b', 'r'),
89 	 "dmap.supportsbrowse", "msbr", DMAP_TYPE_BYTE},
90 	{DMAP_CC_MSQY, MAKE_CONTENT_CODE ('m', 's', 'q', 'y'),
91 	 "dmap.supportsquery", "msqy", DMAP_TYPE_BYTE},
92 	{DMAP_CC_MSIX, MAKE_CONTENT_CODE ('m', 's', 'i', 'x'),
93 	 "dmap.supportsindex", "msix", DMAP_TYPE_BYTE},
94 	{DMAP_CC_MSRS, MAKE_CONTENT_CODE ('m', 's', 'r', 's'),
95 	 "dmap.supportsresolve", "msrs", DMAP_TYPE_BYTE},
96 	{DMAP_CC_MSTM, MAKE_CONTENT_CODE ('m', 's', 't', 'm'),
97 	 "dmap.timeoutinterval", "mstm", DMAP_TYPE_INT},
98 	{DMAP_CC_MSDC, MAKE_CONTENT_CODE ('m', 's', 'd', 'c'),
99 	 "dmap.databasescount", "msdc", DMAP_TYPE_INT},
100 	{DMAP_CC_MCCR, MAKE_CONTENT_CODE ('m', 'c', 'c', 'r'),
101 	 "dmap.contentcodesresponse", "mccr", DMAP_TYPE_CONTAINER},
102 	{DMAP_CC_MCNM, MAKE_CONTENT_CODE ('m', 'c', 'n', 'm'),
103 	 "dmap.contentcodesnumber", "mcnm", DMAP_TYPE_INT},
104 	{DMAP_CC_MCNA, MAKE_CONTENT_CODE ('m', 'c', 'n', 'a'),
105 	 "dmap.contentcodesname", "mcna", DMAP_TYPE_STRING},
106 	{DMAP_CC_MCTY, MAKE_CONTENT_CODE ('m', 'c', 't', 'y'),
107 	 "dmap.contentcodestype", "mcty", DMAP_TYPE_SHORT},
108 	{DMAP_CC_MLOG, MAKE_CONTENT_CODE ('m', 'l', 'o', 'g'),
109 	 "dmap.loginresponse", "mlog", DMAP_TYPE_CONTAINER},
110 	{DMAP_CC_MLID, MAKE_CONTENT_CODE ('m', 'l', 'i', 'd'),
111 	 "dmap.sessionid", "mlid", DMAP_TYPE_INT},
112 	{DMAP_CC_MUPD, MAKE_CONTENT_CODE ('m', 'u', 'p', 'd'),
113 	 "dmap.updateresponse", "mupd", DMAP_TYPE_CONTAINER},
114 	{DMAP_CC_MUSR, MAKE_CONTENT_CODE ('m', 'u', 's', 'r'),
115 	 "dmap.serverrevision", "musr", DMAP_TYPE_INT},
116 	{DMAP_CC_MUTY, MAKE_CONTENT_CODE ('m', 'u', 't', 'y'),
117 	 "dmap.updatetype", "muty", DMAP_TYPE_BYTE},
118 	{DMAP_CC_MUDL, MAKE_CONTENT_CODE ('m', 'u', 'd', 'l'),
119 	 "dmap.deletedidlisting", "mudl", DMAP_TYPE_CONTAINER},
120 	{DMAP_CC_MSMA, MAKE_CONTENT_CODE ('m', 's', 'm', 'a'),
121 	 "dmap.speakermachineaddress", "msma", DMAP_TYPE_INT},
122 	{DMAP_CC_FQUESCH, MAKE_CONTENT_CODE ('f', '?', 'c', 'h'),
123 	 "dmap.haschildcontainers", "f?ch", DMAP_TYPE_BYTE},
124 
125 	{DMAP_CC_APRO, MAKE_CONTENT_CODE ('a', 'p', 'r', 'o'),
126 	 "daap.protocolversion", "apro", DMAP_TYPE_VERSION},
127 	{DMAP_CC_AVDB, MAKE_CONTENT_CODE ('a', 'v', 'd', 'b'),
128 	 "daap.serverdatabases", "avdb", DMAP_TYPE_CONTAINER},
129 	{DMAP_CC_ABRO, MAKE_CONTENT_CODE ('a', 'b', 'r', 'o'),
130 	 "daap.databasebrowse", "abro", DMAP_TYPE_CONTAINER},
131 	{DMAP_CC_ABAL, MAKE_CONTENT_CODE ('a', 'b', 'a', 'l'),
132 	 "daap.browsealbumlisting", "abal", DMAP_TYPE_CONTAINER},
133 	{DMAP_CC_ABAR, MAKE_CONTENT_CODE ('a', 'b', 'a', 'r'),
134 	 "daap.browseartistlisting", "abar", DMAP_TYPE_CONTAINER},
135 	{DMAP_CC_ABCP, MAKE_CONTENT_CODE ('a', 'b', 'c', 'p'),
136 	 "daap.browsecomposerlisting", "abcp", DMAP_TYPE_CONTAINER},
137 	{DMAP_CC_ABGN, MAKE_CONTENT_CODE ('a', 'b', 'g', 'n'),
138 	 "daap.browsegenrelisting", "abgn", DMAP_TYPE_CONTAINER},
139 	{DMAP_CC_ADBS, MAKE_CONTENT_CODE ('a', 'd', 'b', 's'),
140 	 "daap.returndatabasesongs", "adbs", DMAP_TYPE_CONTAINER},
141 	{DMAP_CC_ASAL, MAKE_CONTENT_CODE ('a', 's', 'a', 'l'),
142 	 "daap.songalbum", "asal", DMAP_TYPE_STRING},
143 	{DMAP_CC_ASAI, MAKE_CONTENT_CODE ('a', 's', 'a', 'i'),
144 	 "daap.songalbumid", "asai", DMAP_TYPE_INT},
145 	{DMAP_CC_ASAA, MAKE_CONTENT_CODE ('a', 's', 'a', 'a'),
146 	 "daap.songalbumartist", "asaa", DMAP_TYPE_STRING},
147 	{DMAP_CC_ASAR, MAKE_CONTENT_CODE ('a', 's', 'a', 'r'),
148 	 "daap.songartist", "asar", DMAP_TYPE_STRING},
149 	{DMAP_CC_ASBT, MAKE_CONTENT_CODE ('a', 's', 'b', 't'),
150 	 "daap.songsbeatsperminute", "asbt", DMAP_TYPE_SHORT},
151 	{DMAP_CC_ASBR, MAKE_CONTENT_CODE ('a', 's', 'b', 'r'),
152 	 "daap.songbitrate", "asbr", DMAP_TYPE_SHORT},
153 	{DMAP_CC_ASCM, MAKE_CONTENT_CODE ('a', 's', 'c', 'm'),
154 	 "daap.songcomment", "ascm", DMAP_TYPE_STRING},
155 	{DMAP_CC_ASCO, MAKE_CONTENT_CODE ('a', 's', 'c', 'o'),
156 	 "daap.songcompliation", "asco", DMAP_TYPE_BYTE},
157 	{DMAP_CC_ASDA, MAKE_CONTENT_CODE ('a', 's', 'd', 'a'),
158 	 "daap.songdateadded", "asda", DMAP_TYPE_DATE},
159 	{DMAP_CC_ASDM, MAKE_CONTENT_CODE ('a', 's', 'd', 'm'),
160 	 "daap.songdatemodified", "asdm", DMAP_TYPE_DATE},
161 	{DMAP_CC_ASDC, MAKE_CONTENT_CODE ('a', 's', 'd', 'c'),
162 	 "daap.songdisccount", "asdc", DMAP_TYPE_SHORT},
163 	{DMAP_CC_ASDN, MAKE_CONTENT_CODE ('a', 's', 'd', 'n'),
164 	 "daap.songdiscnumber", "asdn", DMAP_TYPE_SHORT},
165 	{DMAP_CC_ASDB, MAKE_CONTENT_CODE ('a', 's', 'd', 'b'),
166 	 "daap.songdisabled", "asdb", DMAP_TYPE_BYTE},
167 	{DMAP_CC_ASEQ, MAKE_CONTENT_CODE ('a', 's', 'e', 'q'),
168 	 "daap.songeqpreset", "aseq", DMAP_TYPE_STRING},
169 	{DMAP_CC_ASFM, MAKE_CONTENT_CODE ('a', 's', 'f', 'm'),
170 	 "daap.songformat", "asfm", DMAP_TYPE_STRING},
171 	{DMAP_CC_ASGN, MAKE_CONTENT_CODE ('a', 's', 'g', 'n'),
172 	 "daap.songgenre", "asgn", DMAP_TYPE_STRING},
173 	{DMAP_CC_ASDT, MAKE_CONTENT_CODE ('a', 's', 'd', 't'),
174 	 "daap.songdescription", "asdt", DMAP_TYPE_STRING},
175 	{DMAP_CC_ASRV, MAKE_CONTENT_CODE ('a', 's', 'r', 'v'),
176 	 "daap.songrelativevolume", "asrv", DMAP_TYPE_SIGNED_INT},
177 	{DMAP_CC_ASSR, MAKE_CONTENT_CODE ('a', 's', 's', 'r'),
178 	 "daap.songsamplerate", "assr", DMAP_TYPE_INT},
179 	{DMAP_CC_ASSZ, MAKE_CONTENT_CODE ('a', 's', 's', 'z'),
180 	 "daap.songsize", "assz", DMAP_TYPE_INT},
181 	{DMAP_CC_ASST, MAKE_CONTENT_CODE ('a', 's', 's', 't'),
182 	 "daap.songstarttime", "asst", DMAP_TYPE_INT},
183 	{DMAP_CC_ASSP, MAKE_CONTENT_CODE ('a', 's', 's', 'p'),
184 	 "daap.songstoptime", "assp", DMAP_TYPE_INT},
185 	{DMAP_CC_ASTM, MAKE_CONTENT_CODE ('a', 's', 't', 'm'),
186 	 "daap.songtime", "astm", DMAP_TYPE_INT},
187 	{DMAP_CC_ASTC, MAKE_CONTENT_CODE ('a', 's', 't', 'c'),
188 	 "daap.songtrackcount", "astc", DMAP_TYPE_SHORT},
189 	{DMAP_CC_ASTN, MAKE_CONTENT_CODE ('a', 's', 't', 'n'),
190 	 "daap.songtracknumber", "astn", DMAP_TYPE_SHORT},
191 	{DMAP_CC_ASUR, MAKE_CONTENT_CODE ('a', 's', 'u', 'r'),
192 	 "daap.songuserrating", "asur", DMAP_TYPE_BYTE},
193 	{DMAP_CC_ASYR, MAKE_CONTENT_CODE ('a', 's', 'y', 'r'),
194 	 "daap.songyear", "asyr", DMAP_TYPE_SHORT},
195 	{DMAP_CC_ASDK, MAKE_CONTENT_CODE ('a', 's', 'd', 'k'),
196 	 "daap.songdatakind", "asdk", DMAP_TYPE_BYTE},
197 	{DMAP_CC_ASUL, MAKE_CONTENT_CODE ('a', 's', 'u', 'l'),
198 	 "daap.songdataurl", "asul", DMAP_TYPE_STRING},
199 	{DMAP_CC_ASSU, MAKE_CONTENT_CODE ('a', 's', 's', 'u'),
200 	 "daap.sortalbum", "assu", DMAP_TYPE_STRING},
201 	{DMAP_CC_ASSA, MAKE_CONTENT_CODE ('a', 's', 's', 'a'),
202 	 "daap.sortartist", "assa", DMAP_TYPE_STRING},
203 	{DMAP_CC_APLY, MAKE_CONTENT_CODE ('a', 'p', 'l', 'y'),
204 	 "daap.databaseplaylists", "aply", DMAP_TYPE_CONTAINER},
205 	{DMAP_CC_ABPL, MAKE_CONTENT_CODE ('a', 'b', 'p', 'l'),
206 	 "daap.baseplaylist", "abpl", DMAP_TYPE_BYTE},
207 	{DMAP_CC_APSO, MAKE_CONTENT_CODE ('a', 'p', 's', 'o'),
208 	 "daap.playlistsongs", "apso", DMAP_TYPE_CONTAINER},
209 	{DMAP_CC_PRSV, MAKE_CONTENT_CODE ('p', 'r', 's', 'v'), "daap.resolve",
210 	 "prsv", DMAP_TYPE_CONTAINER},
211 	{DMAP_CC_ARIF, MAKE_CONTENT_CODE ('a', 'r', 'i', 'f'),
212 	 "daap.resolveinfo", "arif", DMAP_TYPE_CONTAINER},
213 	{DMAP_CC_MSAS, MAKE_CONTENT_CODE ('m', 's', 'a', 's'),
214 	 "daap.authentication.schemes", "msas", DMAP_TYPE_BYTE},
215 	{DMAP_CC_AGRP, MAKE_CONTENT_CODE ('a', 'g', 'r', 'p'),
216 	 "daap.songgrouping", "agrp", DMAP_TYPE_STRING},
217 	{DMAP_CC_AGAL, MAKE_CONTENT_CODE ('a', 'g', 'a', 'l'),
218 	 "daap.albumgrouping", "agal", DMAP_TYPE_CONTAINER},
219 	{DMAP_CC_ASCP, MAKE_CONTENT_CODE ('a', 's', 'c', 'p'),
220 	 "daap.songcomposer", "ascp", DMAP_TYPE_STRING},
221 	{DMAP_CC_PPRO, MAKE_CONTENT_CODE ('p', 'p', 'r', 'o'),
222 	 "dpap.protocolversion", "ppro", DMAP_TYPE_VERSION},
223 	{DMAP_CC_PASP, MAKE_CONTENT_CODE ('p', 'a', 's', 'p'),
224 	 "dpap.aspectratio", "pasp", DMAP_TYPE_STRING},
225 	{DMAP_CC_PFDT, MAKE_CONTENT_CODE ('p', 'f', 'd', 't'),
226 	 "dpap.filedata", "pfdt", DMAP_TYPE_POINTER},
227 	{DMAP_CC_PICD, MAKE_CONTENT_CODE ('p', 'i', 'c', 'd'),
228 	 "dpap.creationdate", "picd", DMAP_TYPE_INT},
229 	{DMAP_CC_PIMF, MAKE_CONTENT_CODE ('p', 'i', 'm', 'f'),
230 	 "dpap.imagefilename", "pimf", DMAP_TYPE_STRING},
231 	{DMAP_CC_PFMT, MAKE_CONTENT_CODE ('p', 'f', 'm', 't'),
232 	 "dpap.imageformat", "pfmt", DMAP_TYPE_STRING},
233 	{DMAP_CC_PIFS, MAKE_CONTENT_CODE ('p', 'i', 'f', 's'),
234 	 "dpap.imagefilesize", "pifs", DMAP_TYPE_INT},
235 	{DMAP_CC_PLSZ, MAKE_CONTENT_CODE ('p', 'l', 's', 'z'),
236 	 "dpap.imagelargefilesize", "plsz", DMAP_TYPE_INT},
237 	{DMAP_CC_PHGT, MAKE_CONTENT_CODE ('p', 'h', 'g', 't'),
238 	 "dpap.imagepixelheight", "phgt", DMAP_TYPE_INT},
239 	{DMAP_CC_PWTH, MAKE_CONTENT_CODE ('p', 'w', 't', 'h'),
240 	 "dpap.imagepixelwidth", "pwth", DMAP_TYPE_INT},
241 	{DMAP_CC_PRAT, MAKE_CONTENT_CODE ('p', 'r', 'a', 't'),
242 	 "dpap.imagerating", "prat", DMAP_TYPE_INT},
243 	{DMAP_CC_PCMT, MAKE_CONTENT_CODE ('p', 'c', 'm', 't'),
244 	 "dpap.imagecomments", "pcmt", DMAP_TYPE_STRING},
245 	{DMAP_CC_PRET, MAKE_CONTENT_CODE ('p', 'r', 'e', 't'), "dpap.pret",
246 	 "pret", DMAP_TYPE_STRING},
247 	{DMAP_CC_AESV, MAKE_CONTENT_CODE ('a', 'e', 'S', 'V'),
248 	 "com.apple.itunes.music-sharing-version", "aesv", DMAP_TYPE_INT},
249 	{DMAP_CC_AEHV, MAKE_CONTENT_CODE ('a', 'e', 'H', 'V'),
250 	 "com.apple.itunes.has-video", "aeHV", DMAP_TYPE_BYTE},
251 	{DMAP_CC_AESP, MAKE_CONTENT_CODE ('a', 'e', 'S', 'P'),
252 	 "com.apple.itunes.smart-playlist", "aeSP", DMAP_TYPE_BYTE},
253 	{DMAP_CC_AEPP, MAKE_CONTENT_CODE ('a', 'e', 'P', 'P'),
254 	 "com.apple.itunes.is-podcast-playlist", "aePP", DMAP_TYPE_BYTE},
255 	{DMAP_CC_AEPS, MAKE_CONTENT_CODE ('a', 'e', 'P', 'S'),
256 	 "com.apple.itunes.special-playlist", "aePS", DMAP_TYPE_BYTE},
257 	{DMAP_CC_AESG, MAKE_CONTENT_CODE ('a', 'e', 'S', 'G'),
258 	 "com.apple.itunes.saved-genius", "aeSG", DMAP_TYPE_BYTE},
259 	{DMAP_CC_AEMK, MAKE_CONTENT_CODE ('a', 'e', 'M', 'K'),
260 	 "com.apple.itunes.mediakind", "aeMK", DMAP_TYPE_BYTE},
261 	{DMAP_CC_AEFP, MAKE_CONTENT_CODE ('a', 'e', 'F', 'P'),
262 	 "com.apple.itunes.req-fplay", "aeFP", DMAP_TYPE_BYTE},
263 
264 	/* DACP */
265 	{DMAP_CC_CMPA, MAKE_CONTENT_CODE ('c', 'm', 'p', 'a'),
266 	 "dacp.contentcontainer", "cmpa", DMAP_TYPE_CONTAINER},
267 	{DMAP_CC_CMNM, MAKE_CONTENT_CODE ('c', 'm', 'n', 'm'),
268 	 "dacp.contentname", "cmnm", DMAP_TYPE_STRING},
269 	{DMAP_CC_CMTY, MAKE_CONTENT_CODE ('c', 'm', 't', 'y'),
270 	 "dacp.contentvalue", "cmty", DMAP_TYPE_STRING},
271 	{DMAP_CC_CMPG, MAKE_CONTENT_CODE ('c', 'm', 'p', 'g'),
272 	 "dacp.passguid", "cmpg", DMAP_TYPE_INT64},
273 
274 	{DMAP_CC_CACI, MAKE_CONTENT_CODE ('c', 'a', 'c', 'i'),
275 	 "dacp.controlint", "caci", DMAP_TYPE_CONTAINER},
276 	{DMAP_CC_CAPS, MAKE_CONTENT_CODE ('c', 'a', 'p', 's'),
277 	 "dacp.playstatus", "caps", DMAP_TYPE_BYTE},
278 	{DMAP_CC_CASH, MAKE_CONTENT_CODE ('c', 'a', 's', 'h'),
279 	 "dacp.shufflestate", "cash", DMAP_TYPE_BYTE},
280 	{DMAP_CC_CARP, MAKE_CONTENT_CODE ('c', 'a', 'r', 'p'),
281 	 "dacp.repeatstate", "carp", DMAP_TYPE_BYTE},
282 	{DMAP_CC_CAAS, MAKE_CONTENT_CODE ('c', 'a', 'a', 's'),
283 	 "dacp.albumshuffle", "caas", DMAP_TYPE_INT},
284 	{DMAP_CC_CAAR, MAKE_CONTENT_CODE ('c', 'a', 'a', 'r'),
285 	 "dacp.albumrepeat", "caar", DMAP_TYPE_INT},
286 	{DMAP_CC_CAIA, MAKE_CONTENT_CODE ('c', 'a', 'i', 'a'),
287 	 "dacp.isavailiable", "caia", DMAP_TYPE_BYTE},
288 	{DMAP_CC_CANP, MAKE_CONTENT_CODE ('c', 'a', 'n', 'p'),
289 	 "dacp.nowplaying", "canp", DMAP_TYPE_INT64},
290 	{DMAP_CC_CANN, MAKE_CONTENT_CODE ('c', 'a', 'n', 'n'),
291 	 "dacp.nowplayingtrack", "cann", DMAP_TYPE_STRING},
292 	{DMAP_CC_CANA, MAKE_CONTENT_CODE ('c', 'a', 'n', 'a'),
293 	 "dacp.nowplayingartist", "cana", DMAP_TYPE_STRING},
294 	{DMAP_CC_CANL, MAKE_CONTENT_CODE ('c', 'a', 'n', 'l'),
295 	 "dacp.nowplayingalbum", "canl", DMAP_TYPE_STRING},
296 	{DMAP_CC_CANG, MAKE_CONTENT_CODE ('c', 'a', 'n', 'g'),
297 	 "dacp.nowplayinggenre", "cang", DMAP_TYPE_STRING},
298 	{DMAP_CC_CANT, MAKE_CONTENT_CODE ('c', 'a', 'n', 't'),
299 	 "dacp.remainingtime", "cant", DMAP_TYPE_INT},
300 	{DMAP_CC_CASP, MAKE_CONTENT_CODE ('c', 'a', 's', 'p'),
301 	 "dacp.speakers", "casp", DMAP_TYPE_CONTAINER},
302 	{DMAP_CC_CASS, MAKE_CONTENT_CODE ('c', 'a', 's', 's'), "dacp.ss",
303 	 "cass", DMAP_TYPE_BYTE},
304 	{DMAP_CC_CAST, MAKE_CONTENT_CODE ('c', 'a', 's', 't'),
305 	 "dacp.tracklength", "cast", DMAP_TYPE_INT},
306 	{DMAP_CC_CASU, MAKE_CONTENT_CODE ('c', 'a', 's', 'u'), "dacp.su",
307 	 "casu", DMAP_TYPE_BYTE},
308 	{DMAP_CC_CASG, MAKE_CONTENT_CODE ('c', 'a', 's', 'g'), "dacp.sg",
309 	 "caSG", DMAP_TYPE_BYTE},
310 	{DMAP_CC_CACR, MAKE_CONTENT_CODE ('c', 'a', 'c', 'r'), "dacp.cacr",
311 	 "cacr", DMAP_TYPE_CONTAINER},
312 
313 	{DMAP_CC_CMCP, MAKE_CONTENT_CODE ('c', 'm', 'c', 'p'),
314 	 "dmcp.controlprompt", "cmcp", DMAP_TYPE_CONTAINER},
315 	{DMAP_CC_CMGT, MAKE_CONTENT_CODE ('c', 'm', 'g', 't'),
316 	 "dmcp.getpropertyresponse", "cmgt", DMAP_TYPE_CONTAINER},
317 	{DMAP_CC_CMIK, MAKE_CONTENT_CODE ('c', 'm', 'i', 'k'), "dmcp.ik",
318 	 "cmik", DMAP_TYPE_BYTE},
319 	{DMAP_CC_CMSP, MAKE_CONTENT_CODE ('c', 'm', 's', 'p'), "dmcp.ik",
320 	 "cmsp", DMAP_TYPE_BYTE},
321 	{DMAP_CC_CMST, MAKE_CONTENT_CODE ('c', 'm', 's', 't'), "dmcp.status",
322 	 "cmst", DMAP_TYPE_CONTAINER},
323 	{DMAP_CC_CMSV, MAKE_CONTENT_CODE ('c', 'm', 's', 'v'), "dmcp.sv",
324 	 "cmsv", DMAP_TYPE_BYTE},
325 	{DMAP_CC_CMSR, MAKE_CONTENT_CODE ('c', 'm', 's', 'r'),
326 	 "dmcp.mediarevision", "cmsr", DMAP_TYPE_INT},
327 	{DMAP_CC_CMMK, MAKE_CONTENT_CODE ('c', 'm', 'm', 'k'),
328 	 "dmcp.mediakind", "cmmk", DMAP_TYPE_INT},
329 	{DMAP_CC_CMVO, MAKE_CONTENT_CODE ('c', 'm', 'v', 'o'), "dmcp.volume",
330 	 "cmvo", DMAP_TYPE_INT},
331 
332 	{DMAP_CC_CMPR, MAKE_CONTENT_CODE ('c', 'm', 'p', 'r'), "dacp.unknown",
333 	 "cmpr", DMAP_TYPE_INT},
334 	{DMAP_CC_CAPR, MAKE_CONTENT_CODE ('c', 'a', 'p', 'r'), "dacp.unknown",
335 	 "capr", DMAP_TYPE_INT},
336 	{DMAP_CC_AEFR, MAKE_CONTENT_CODE ('a', 'e', 'F', 'R'), "dacp.unknown",
337 	 "aeFR", DMAP_TYPE_BYTE},
338 	{DMAP_CC_CAOV, MAKE_CONTENT_CODE ('c', 'a', 'o', 'v'), "dacp.unknown",
339 	 "caov", DMAP_TYPE_BYTE},
340 	{DMAP_CC_CMRL, MAKE_CONTENT_CODE ('c', 'm', 'r', 'l'), "dacp.unknown",
341 	 "cmrl", DMAP_TYPE_BYTE},
342 	{DMAP_CC_CAHP, MAKE_CONTENT_CODE ('c', 'a', 'h', 'p'), "dacp.unknown",
343 	 "cahp", DMAP_TYPE_BYTE},
344 	{DMAP_CC_CAIV, MAKE_CONTENT_CODE ('c', 'a', 'i', 'v'), "dacp.unknown",
345 	 "caiv", DMAP_TYPE_BYTE},
346 	{DMAP_CC_CAVC, MAKE_CONTENT_CODE ('c', 'a', 'v', 'c'), "dacp.unknwon",
347 	 "cavc", DMAP_TYPE_BYTE},
348 
349 };
350 
351 const gchar *
dmap_content_code_name(DMAPContentCode code)352 dmap_content_code_name (DMAPContentCode code)
353 {
354 	return cc_defs[code - 1].name;
355 }
356 
357 DMAPType
dmap_content_code_dmap_type(DMAPContentCode code)358 dmap_content_code_dmap_type (DMAPContentCode code)
359 {
360 	return cc_defs[code - 1].type;
361 }
362 
363 const gchar *
dmap_content_code_string(DMAPContentCode code)364 dmap_content_code_string (DMAPContentCode code)
365 {
366 	return cc_defs[code - 1].string;
367 }
368 
369 static GType
dmap_content_code_gtype(DMAPContentCode code)370 dmap_content_code_gtype (DMAPContentCode code)
371 {
372 	switch (dmap_content_code_dmap_type (code)) {
373 	case DMAP_TYPE_BYTE:
374 	case DMAP_TYPE_SIGNED_INT:
375 		return G_TYPE_CHAR;
376 	case DMAP_TYPE_SHORT:
377 	case DMAP_TYPE_INT:
378 	case DMAP_TYPE_DATE:
379 		return G_TYPE_INT;
380 	case DMAP_TYPE_INT64:
381 		return G_TYPE_INT64;
382 	case DMAP_TYPE_VERSION:
383 		return G_TYPE_DOUBLE;
384 	case DMAP_TYPE_STRING:
385 		return G_TYPE_STRING;
386 	case DMAP_TYPE_POINTER:
387 		return G_TYPE_POINTER;
388 	case DMAP_TYPE_CONTAINER:
389 	default:
390 		return G_TYPE_NONE;
391 	}
392 }
393 
394 static gboolean
dmap_structure_node_serialize(GNode * node,GByteArray * array)395 dmap_structure_node_serialize (GNode * node, GByteArray * array)
396 {
397 	DMAPStructureItem *item = node->data;
398 	DMAPType dmap_type;
399 	guint32 size = GINT32_TO_BE (item->size);
400 
401 	if (item->content_code != DMAP_RAW) {
402 		g_byte_array_append (array,
403 				     (const guint8 *)
404 				     dmap_content_code_string (item->
405 							       content_code),
406 				     4);
407 		g_byte_array_append (array, (const guint8 *) &size, 4);
408 	}
409 
410 	dmap_type = dmap_content_code_dmap_type (item->content_code);
411 
412 	switch (dmap_type) {
413 	case DMAP_TYPE_BYTE:
414 	case DMAP_TYPE_SIGNED_INT:{
415 			gchar c = g_value_get_schar (&(item->content));
416 
417 			g_byte_array_append (array, (const guint8 *) &c, 1);
418 
419 			break;
420 		}
421 	case DMAP_TYPE_SHORT:{
422 			gint32 i = g_value_get_int (&(item->content));
423 			gint16 s = GINT16_TO_BE ((gint16) i);
424 
425 			g_byte_array_append (array, (const guint8 *) &s, 2);
426 
427 			break;
428 		}
429 	case DMAP_TYPE_DATE:
430 	case DMAP_TYPE_INT:{
431 			gint32 i = g_value_get_int (&(item->content));
432 			gint32 s = GINT32_TO_BE (i);
433 
434 			g_byte_array_append (array, (const guint8 *) &s, 4);
435 
436 			break;
437 		}
438 	case DMAP_TYPE_VERSION:{
439 			gdouble v = g_value_get_double (&(item->content));
440 			gint16 major;
441 			gint8 minor;
442 			gint8 patch = 0;
443 
444 			major = (gint16) v;
445 			minor = (gint8) (v - ((gdouble) major));
446 
447 			major = GINT16_TO_BE (major);
448 
449 			g_byte_array_append (array, (const guint8 *) &major,
450 					     2);
451 			g_byte_array_append (array, (const guint8 *) &minor,
452 					     1);
453 			g_byte_array_append (array, (const guint8 *) &patch,
454 					     1);
455 
456 			break;
457 		}
458 	case DMAP_TYPE_INT64:{
459 			gint64 i = g_value_get_int64 (&(item->content));
460 			gint64 s = GINT64_TO_BE (i);
461 
462 			g_byte_array_append (array, (const guint8 *) &s, 8);
463 
464 			break;
465 		}
466 	case DMAP_TYPE_STRING:{
467 			const gchar *s =
468 				g_value_get_string (&(item->content));
469 
470 			g_byte_array_append (array, (const guint8 *) s,
471 					     strlen (s));
472 
473 			break;
474 		}
475 	case DMAP_TYPE_POINTER:{
476 			const gpointer *data =
477 				g_value_get_pointer (&(item->content));
478 
479 			g_byte_array_append (array, (const guint8 *) data,
480 					     item->size);
481 
482 			break;
483 		}
484 	case DMAP_TYPE_CONTAINER:
485 	default:
486 		break;
487 	}
488 
489 	return FALSE;
490 }
491 
492 gchar *
dmap_structure_serialize(GNode * structure,guint * length)493 dmap_structure_serialize (GNode * structure, guint * length)
494 {
495 	GByteArray *array;
496 	gchar *data;
497 
498 	array = g_byte_array_new ();
499 
500 	if (structure) {
501 		g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
502 				 (GNodeTraverseFunc)
503 				 dmap_structure_node_serialize, array);
504 	}
505 
506 	data = (gchar *) array->data;
507 	*length = array->len;
508 	g_byte_array_free (array, FALSE);
509 
510 	return data;
511 }
512 
513 DMAPContentCode
dmap_content_code_read_from_buffer(const gchar * buf)514 dmap_content_code_read_from_buffer (const gchar * buf)
515 {
516 	gint32 c = MAKE_CONTENT_CODE (buf[0], buf[1], buf[2], buf[3]);
517 	guint i;
518 
519 	for (i = 0; i < G_N_ELEMENTS (cc_defs); i++) {
520 		if (cc_defs[i].int_code == c) {
521 			return cc_defs[i].code;
522 		}
523 	}
524 
525 	g_warning ("Content code %4s is invalid.", buf);
526 
527 	return DMAP_CC_INVALID;
528 }
529 
530 static gchar *
dmap_buffer_read_string(const gchar * buf,gssize size)531 dmap_buffer_read_string (const gchar * buf, gssize size)
532 {
533 	if (g_utf8_validate (buf, size, NULL) == TRUE) {
534 		return g_strndup (buf, size);
535 	} else {
536 		return g_strdup ("");
537 	}
538 }
539 
540 static void
dmap_structure_parse_container_buffer(GNode * parent,const guchar * buf,gint buf_length)541 dmap_structure_parse_container_buffer (GNode * parent,
542 				       const guchar * buf, gint buf_length)
543 {
544 	gint l = 0;
545 
546 	while (l < buf_length) {
547 		DMAPContentCode cc;
548 		gint codesize = 0;
549 		DMAPStructureItem *item = NULL;
550 		GNode *node = NULL;
551 		GType gtype;
552 
553 		/* we need at least 8 bytes, 4 of content_code and 4 of size */
554 		if (buf_length - l < 8) {
555 			g_debug ("Malformed response received\n");
556 			return;
557 		}
558 
559 		cc = dmap_content_code_read_from_buffer ((const gchar *)
560 							 &(buf[l]));
561 		if (cc == DMAP_CC_INVALID) {
562 			return;
563 		}
564 		l += 4;
565 
566 		codesize = DMAP_READ_UINT32_BE (&(buf[l]));
567 		/* CCCCSIZECONTENT
568 		 * if the buffer length (minus 8 for the content code & size)
569 		 * is smaller than the read codesize (ie, someone sent us
570 		 * a codesize that is larger than the remaining data)
571 		 * then get out before we start processing it
572 		 */
573 		if (codesize > buf_length - l - 4 || codesize < 0) {
574 			g_debug ("Invalid codesize %d received in buf_length %d\n", codesize, buf_length);
575 			return;
576 		}
577 		l += 4;
578 
579 		item = g_new0 (DMAPStructureItem, 1);
580 		item->content_code = cc;
581 		node = g_node_new (item);
582 		g_node_append (parent, node);
583 
584 		gtype = dmap_content_code_gtype (item->content_code);
585 
586 		if (gtype != G_TYPE_NONE) {
587 			g_value_init (&(item->content), gtype);
588 		}
589 // FIXME USE THE G_TYPE CONVERTOR FUNCTION dmap_type_to_gtype
590 		switch (dmap_content_code_dmap_type (item->content_code)) {
591 		case DMAP_TYPE_SIGNED_INT:
592 		case DMAP_TYPE_BYTE:{
593 				gchar c = 0;
594 
595 				if (codesize == 1) {
596 					c = (gchar)
597 						DMAP_READ_UINT8 (&(buf[l]));
598 				}
599 
600 				item->size = 1;
601 				g_value_set_schar (&(item->content), c);
602 				break;
603 			}
604 		case DMAP_TYPE_SHORT:{
605 				gint16 s = 0;
606 
607 				if (codesize == 2) {
608 					s = DMAP_READ_UINT16_BE (&(buf[l]));
609 				}
610 
611 				item->size = 2;
612 				g_value_set_int (&(item->content),
613 						 (gint32) s);
614 				break;
615 			}
616 		case DMAP_TYPE_DATE:
617 		case DMAP_TYPE_INT:{
618 				gint32 i = 0;
619 
620 				if (codesize == 4) {
621 					i = DMAP_READ_UINT32_BE (&(buf[l]));
622 				}
623 
624 				item->size = 4;
625 				g_value_set_int (&(item->content), i);
626 				break;
627 			}
628 		case DMAP_TYPE_INT64:{
629 				gint64 i = 0;
630 
631 				if (codesize == 8) {
632 					i = DMAP_READ_UINT64_BE (&(buf[l]));
633 				}
634 
635 				item->size = 8;
636 				g_value_set_int64 (&(item->content), i);
637 				break;
638 			}
639 		case DMAP_TYPE_STRING:{
640 				gchar *s =
641 					dmap_buffer_read_string ((const gchar
642 								  *)
643 								 &(buf[l]),
644 								 codesize);
645 
646 				item->size = strlen (s);
647 				g_value_take_string (&(item->content), s);
648 				break;
649 			}
650 		case DMAP_TYPE_POINTER:{
651 				gpointer *data =
652 					g_memdup ((const gchar *) &(buf[l]),
653 						  codesize);
654 
655 				item->size = codesize;
656 				g_value_set_pointer (&(item->content), data);
657 
658 				break;
659 			}
660 		case DMAP_TYPE_VERSION:{
661 				gint16 major = 0;
662 				gint16 minor = 0;
663 				gint16 patch = 0;
664 				gdouble v = 0;
665 
666 				if (codesize == 4) {
667 					major = DMAP_READ_UINT16_BE (&
668 								     (buf
669 								      [l]));
670 					minor = DMAP_READ_UINT8 (&(buf[l]) +
671 								 2);
672 					patch = DMAP_READ_UINT8 (&(buf[l]) +
673 								 3);
674 				}
675 
676 				v = (gdouble) major;
677 				v += (gdouble) (minor * 0.1);
678 				v += (gdouble) (patch * 0.01);
679 
680 				item->size = 4;
681 				g_value_set_double (&(item->content), v);
682 				break;
683 			}
684 		case DMAP_TYPE_CONTAINER:{
685 				dmap_structure_parse_container_buffer (node,
686 								       &(buf
687 									 [l]),
688 								       codesize);
689 				break;
690 			}
691 		}
692 
693 		l += codesize;
694 	}
695 
696 	return;
697 }
698 
699 GNode *
dmap_structure_parse(const gchar * buf,gint buf_length)700 dmap_structure_parse (const gchar * buf, gint buf_length)
701 {
702 	GNode *root = NULL;
703 	GNode *child = NULL;
704 
705 	root = g_node_new (NULL);
706 
707 	dmap_structure_parse_container_buffer (root, (guchar *) buf,
708 					       buf_length);
709 
710 	child = root->children;
711 	if (child) {
712 		g_node_unlink (child);
713 	}
714 	g_node_destroy (root);
715 
716 	return child;
717 }
718 
719 struct NodeFinder
720 {
721 	DMAPContentCode code;
722 	GNode *node;
723 };
724 
725 static gboolean
gnode_find_node(GNode * node,gpointer data)726 gnode_find_node (GNode * node, gpointer data)
727 {
728 	struct NodeFinder *finder = (struct NodeFinder *) data;
729 	DMAPStructureItem *item = node->data;
730 
731 	if (item->content_code == finder->code) {
732 		finder->node = node;
733 		return TRUE;
734 	}
735 
736 	return FALSE;
737 }
738 
739 DMAPStructureItem *
dmap_structure_find_item(GNode * structure,DMAPContentCode code)740 dmap_structure_find_item (GNode * structure, DMAPContentCode code)
741 {
742 	GNode *node = NULL;
743 
744 	node = dmap_structure_find_node (structure, code);
745 
746 	if (node) {
747 		return node->data;
748 	}
749 
750 	return NULL;
751 }
752 
753 GNode *
dmap_structure_add(GNode * parent,DMAPContentCode cc,...)754 dmap_structure_add (GNode * parent, DMAPContentCode cc, ...)
755 {
756 	DMAPType dmap_type;
757 	GType gtype;
758 	DMAPStructureItem *item;
759 	va_list list;
760 	GNode *node;
761 	gchar *error = NULL;
762 
763 	va_start (list, cc);
764 
765 	dmap_type = dmap_content_code_dmap_type (cc);
766 	gtype = dmap_content_code_gtype (cc);
767 
768 	item = g_new0 (DMAPStructureItem, 1);
769 	item->content_code = cc;
770 
771 	if (gtype != G_TYPE_NONE) {
772 		g_value_init (&(item->content), gtype);
773 	}
774 
775 	if (dmap_type != DMAP_TYPE_STRING && dmap_type != DMAP_TYPE_CONTAINER
776 	    && dmap_type != DMAP_TYPE_POINTER) {
777 		G_VALUE_COLLECT (&(item->content), list,
778 				 G_VALUE_NOCOPY_CONTENTS, &error);
779 		if (error) {
780 			g_warning ("%s", error);
781 			g_free (error);
782 		}
783 	}
784 
785 	switch (dmap_type) {
786 	case DMAP_TYPE_BYTE:
787 	case DMAP_TYPE_SIGNED_INT:
788 		item->size = 1;
789 		break;
790 	case DMAP_TYPE_SHORT:
791 		item->size = 2;
792 		break;
793 	case DMAP_TYPE_DATE:
794 	case DMAP_TYPE_INT:
795 	case DMAP_TYPE_VERSION:
796 		item->size = 4;
797 		break;
798 	case DMAP_TYPE_INT64:
799 		item->size = 8;
800 		break;
801 	case DMAP_TYPE_STRING:{
802 			gchar *s = va_arg (list, gchar *);
803 
804 			g_value_set_string (&(item->content), s);
805 
806 			/* we dont use G_VALUE_COLLECT for this because we also
807 			 * need the length */
808 			item->size = strlen (s);
809 			break;
810 		}
811 	case DMAP_TYPE_POINTER:{
812 			gpointer p = va_arg (list, gpointer);
813 			gint s = va_arg (list, gint);
814 
815 			g_value_set_pointer (&(item->content), p);
816 
817 			/* we dont use G_VALUE_COLLECT for this because we also
818 			 * need the size */
819 			item->size = s;
820 			break;
821 
822 		}
823 	case DMAP_TYPE_CONTAINER:
824 	default:
825 		break;
826 	}
827 
828 	node = g_node_new (item);
829 
830 	if (parent) {
831 		g_node_append (parent, node);
832 
833 		while (parent) {
834 			DMAPStructureItem *parent_item = parent->data;
835 
836 			if (cc == DMAP_RAW)
837 				parent_item->size += item->size;
838 			else
839 				parent_item->size += (4 + 4 + item->size);
840 
841 			parent = parent->parent;
842 		}
843 	}
844 
845 	return node;
846 }
847 
848 GNode *
dmap_structure_find_node(GNode * structure,DMAPContentCode code)849 dmap_structure_find_node (GNode * structure, DMAPContentCode code)
850 {
851 	struct NodeFinder *finder;
852 	GNode *node = NULL;
853 
854 	finder = g_new0 (struct NodeFinder, 1);
855 
856 	finder->code = code;
857 
858 	g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1,
859 			 gnode_find_node, finder);
860 
861 	node = finder->node;
862 	g_free (finder);
863 	finder = NULL;
864 
865 	return node;
866 }
867 
868 static void
dmap_item_free(DMAPStructureItem * item)869 dmap_item_free (DMAPStructureItem * item)
870 {
871 	if (dmap_content_code_dmap_type (item->content_code) !=
872 	    DMAP_TYPE_CONTAINER) {
873 		g_value_unset (&(item->content));
874 	}
875 
876 	g_free (item);
877 }
878 
879 static gboolean
gnode_free_dmap_item(GNode * node,G_GNUC_UNUSED gpointer data)880 gnode_free_dmap_item (GNode * node, G_GNUC_UNUSED gpointer data)
881 {
882 	dmap_item_free ((DMAPStructureItem *) node->data);
883 
884 	return FALSE;
885 }
886 
887 void
dmap_structure_destroy(GNode * structure)888 dmap_structure_destroy (GNode * structure)
889 {
890 	if (structure) {
891 		g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1,
892 				 gnode_free_dmap_item, NULL);
893 
894 		g_node_destroy (structure);
895 
896 		structure = NULL;
897 	}
898 }
899 
900 const DMAPContentCodeDefinition *
dmap_content_codes(guint * number)901 dmap_content_codes (guint * number)
902 {
903 	*number = G_N_ELEMENTS (cc_defs);
904 
905 	return cc_defs;
906 }
907 
908 gint32
dmap_content_code_string_as_int32(const gchar * str)909 dmap_content_code_string_as_int32 (const gchar * str)
910 {
911 	union
912 	{
913 		gint32 i;
914 		gchar str[5];
915 	} u;
916 
917 	strncpy (u.str, str, 4);
918 	u.str[4] = 0x00;
919 
920 	return g_htonl (u.i);
921 }
922 
923 static gboolean
print_dmap_item(GNode * node,G_GNUC_UNUSED gpointer data)924 print_dmap_item (GNode * node, G_GNUC_UNUSED gpointer data)
925 {
926 	DMAPStructureItem *item;
927 	const gchar *name;
928 	gchar *value;
929 	guint i;
930 
931 	for (i = 1; i < g_node_depth (node); i++) {
932 		g_print ("\t");
933 	}
934 
935 	item = node->data;
936 
937 	name = dmap_content_code_name (item->content_code);
938 
939 	if (G_IS_VALUE (&(item->content))) {
940 		value = g_strdup_value_contents (&(item->content));
941 	} else {
942 		value = g_strdup ("");
943 	}
944 
945 	g_print ("%d, %s = %s (%d)\n", g_node_depth (node), name, value,
946 		 item->size);
947 	g_free (value);
948 
949 	return FALSE;
950 }
951 
952 void
dmap_structure_print(GNode * structure)953 dmap_structure_print (GNode * structure)
954 {
955 	if (structure) {
956 		g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
957 				 (GNodeTraverseFunc) print_dmap_item, NULL);
958 	}
959 }
960 
961 guint
dmap_structure_get_size(GNode * structure)962 dmap_structure_get_size (GNode * structure)
963 {
964 	DMAPStructureItem *item = (DMAPStructureItem *) structure->data;
965 
966 	g_assert (strlen(cc_defs[item->content_code].string) == 4);
967 	g_assert (sizeof(item->size) == 4);
968 
969 	return item->size + strlen(cc_defs[item->content_code].string) + sizeof(item->size);
970 }
971 
972 void
dmap_structure_increase_by_predicted_size(GNode * structure,guint size)973 dmap_structure_increase_by_predicted_size (GNode * structure, guint size)
974 {
975 	((DMAPStructureItem *) structure->data)->size += size;
976 }
977