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