dmap_parser.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. #include "dmap_parser.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <ctype.h>
  6. #define DMAP_STRINGIFY_(x) #x
  7. #define DMAP_STRINGIFY(x) DMAP_STRINGIFY_(x)
  8. typedef enum {
  9. DMAP_UNKNOWN,
  10. DMAP_UINT,
  11. DMAP_INT,
  12. DMAP_STR,
  13. DMAP_DATA,
  14. DMAP_DATE,
  15. DMAP_VERS,
  16. DMAP_DICT,
  17. DMAP_ITEM
  18. } DMAP_TYPE;
  19. typedef struct {
  20. /**
  21. * The four-character code used in the encoded message.
  22. */
  23. const char *code;
  24. /**
  25. * The type of data associated with the content code.
  26. */
  27. DMAP_TYPE type;
  28. /**
  29. * For listings, the type of their listing item children.
  30. *
  31. * Listing items (mlit) can be of any type, and as with other content codes
  32. * their type information is not encoded in the message. Parsers must
  33. * determine the type of the listing items based on their parent context.
  34. */
  35. DMAP_TYPE list_item_type;
  36. /**
  37. * A human-readable name for the content code.
  38. */
  39. const char *name;
  40. } dmap_field;
  41. static const dmap_field dmap_fields[] = {
  42. { "abal", DMAP_DICT, DMAP_STR, "daap.browsealbumlisting" },
  43. { "abar", DMAP_DICT, DMAP_STR, "daap.browseartistlisting" },
  44. { "abcp", DMAP_DICT, DMAP_STR, "daap.browsecomposerlisting" },
  45. { "abgn", DMAP_DICT, DMAP_STR, "daap.browsegenrelisting" },
  46. #ifdef DMAP_FULL
  47. { "abpl", DMAP_UINT, 0, "daap.baseplaylist" },
  48. { "abro", DMAP_DICT, 0, "daap.databasebrowse" },
  49. { "adbs", DMAP_DICT, 0, "daap.databasesongs" },
  50. { "aeAD", DMAP_DICT, 0, "com.apple.itunes.adam-ids-array" },
  51. { "aeAI", DMAP_UINT, 0, "com.apple.itunes.itms-artistid" },
  52. { "aeCD", DMAP_DATA, 0, "com.apple.itunes.flat-chapter-data" },
  53. { "aeCF", DMAP_UINT, 0, "com.apple.itunes.cloud-flavor-id" },
  54. { "aeCI", DMAP_UINT, 0, "com.apple.itunes.itms-composerid" },
  55. { "aeCK", DMAP_UINT, 0, "com.apple.itunes.cloud-library-kind" },
  56. { "aeCM", DMAP_UINT, 0, "com.apple.itunes.cloud-match-type" },
  57. { "aeCR", DMAP_STR, 0, "com.apple.itunes.content-rating" } ,
  58. { "aeCS", DMAP_UINT, 0, "com.apple.itunes.artworkchecksum" },
  59. { "aeCU", DMAP_UINT, 0, "com.apple.itunes.cloud-user-id" },
  60. { "aeCd", DMAP_UINT, 0, "com.apple.itunes.cloud-id" },
  61. { "aeDE", DMAP_STR, 0, "com.apple.itunes.longest-content-description" },
  62. { "aeDL", DMAP_UINT, 0, "com.apple.itunes.drm-downloader-user-id" },
  63. { "aeDP", DMAP_UINT, 0, "com.apple.itunes.drm-platform-id" },
  64. { "aeDR", DMAP_UINT, 0, "com.apple.itunes.drm-user-id" },
  65. { "aeDV", DMAP_UINT, 0, "com.apple.itunes.drm-versions" },
  66. { "aeEN", DMAP_STR, 0, "com.apple.itunes.episode-num-str" },
  67. { "aeES", DMAP_UINT, 0, "com.apple.itunes.episode-sort" },
  68. { "aeFA", DMAP_UINT, 0, "com.apple.itunes.drm-family-id" },
  69. { "aeGD", DMAP_UINT, 0, "com.apple.itunes.gapless-enc-dr" } ,
  70. { "aeGE", DMAP_UINT, 0, "com.apple.itunes.gapless-enc-del" },
  71. { "aeGH", DMAP_UINT, 0, "com.apple.itunes.gapless-heur" },
  72. { "aeGI", DMAP_UINT, 0, "com.apple.itunes.itms-genreid" },
  73. { "aeGR", DMAP_UINT, 0, "com.apple.itunes.gapless-resy" },
  74. { "aeGU", DMAP_UINT, 0, "com.apple.itunes.gapless-dur" },
  75. { "aeGs", DMAP_UINT, 0, "com.apple.itunes.can-be-genius-seed" },
  76. { "aeHC", DMAP_UINT, 0, "com.apple.itunes.has-chapter-data" },
  77. { "aeHD", DMAP_UINT, 0, "com.apple.itunes.is-hd-video" },
  78. { "aeHV", DMAP_UINT, 0, "com.apple.itunes.has-video" },
  79. { "aeK1", DMAP_UINT, 0, "com.apple.itunes.drm-key1-id" },
  80. { "aeK2", DMAP_UINT, 0, "com.apple.itunes.drm-key2-id" },
  81. { "aeMC", DMAP_UINT, 0, "com.apple.itunes.playlist-contains-media-type-count" },
  82. { "aeMK", DMAP_UINT, 0, "com.apple.itunes.mediakind" },
  83. { "aeMX", DMAP_STR, 0, "com.apple.itunes.movie-info-xml" },
  84. { "aeMk", DMAP_UINT, 0, "com.apple.itunes.extended-media-kind" },
  85. { "aeND", DMAP_UINT, 0, "com.apple.itunes.non-drm-user-id" },
  86. { "aeNN", DMAP_STR, 0, "com.apple.itunes.network-name" },
  87. { "aeNV", DMAP_UINT, 0, "com.apple.itunes.norm-volume" },
  88. { "aePC", DMAP_UINT, 0, "com.apple.itunes.is-podcast" },
  89. { "aePI", DMAP_UINT, 0, "com.apple.itunes.itms-playlistid" },
  90. { "aePP", DMAP_UINT, 0, "com.apple.itunes.is-podcast-playlist" },
  91. { "aePS", DMAP_UINT, 0, "com.apple.itunes.special-playlist" },
  92. { "aeRD", DMAP_UINT, 0, "com.apple.itunes.rental-duration" },
  93. { "aeRP", DMAP_UINT, 0, "com.apple.itunes.rental-pb-start" },
  94. { "aeRS", DMAP_UINT, 0, "com.apple.itunes.rental-start" },
  95. { "aeRU", DMAP_UINT, 0, "com.apple.itunes.rental-pb-duration" },
  96. { "aeRf", DMAP_UINT, 0, "com.apple.itunes.is-featured" },
  97. { "aeSE", DMAP_UINT, 0, "com.apple.itunes.store-pers-id" },
  98. { "aeSF", DMAP_UINT, 0, "com.apple.itunes.itms-storefrontid" },
  99. { "aeSG", DMAP_UINT, 0, "com.apple.itunes.saved-genius" },
  100. { "aeSI", DMAP_UINT, 0, "com.apple.itunes.itms-songid" },
  101. { "aeSN", DMAP_STR, 0, "com.apple.itunes.series-name" },
  102. { "aeSP", DMAP_UINT, 0, "com.apple.itunes.smart-playlist" },
  103. { "aeSU", DMAP_UINT, 0, "com.apple.itunes.season-num" },
  104. { "aeSV", DMAP_VERS, 0, "com.apple.itunes.music-sharing-version" },
  105. { "aeXD", DMAP_STR, 0, "com.apple.itunes.xid" },
  106. { "aecp", DMAP_STR, 0, "com.apple.itunes.collection-description" },
  107. { "aels", DMAP_UINT, 0, "com.apple.itunes.liked-state" },
  108. { "aemi", DMAP_DICT, 0, "com.apple.itunes.media-kind-listing-item" },
  109. { "aeml", DMAP_DICT, 0, "com.apple.itunes.media-kind-listing" },
  110. { "agac", DMAP_UINT, 0, "daap.groupalbumcount" },
  111. { "agma", DMAP_UINT, 0, "daap.groupmatchedqueryalbumcount" },
  112. { "agmi", DMAP_UINT, 0, "daap.groupmatchedqueryitemcount" },
  113. { "agrp", DMAP_STR, 0, "daap.songgrouping" },
  114. { "ajAE", DMAP_UINT, 0, "com.apple.itunes.store.ams-episode-type" },
  115. { "ajAS", DMAP_UINT, 0, "com.apple.itunes.store.ams-episode-sort-order" },
  116. { "ajAT", DMAP_UINT, 0, "com.apple.itunes.store.ams-show-type" },
  117. { "ajAV", DMAP_UINT, 0, "com.apple.itunes.store.is-ams-video" },
  118. { "ajal", DMAP_UINT, 0, "com.apple.itunes.store.album-liked-state" },
  119. { "ajcA", DMAP_UINT, 0, "com.apple.itunes.store.show-composer-as-artist" },
  120. { "ajca", DMAP_UINT, 0, "com.apple.itunes.store.show-composer-as-artist" },
  121. { "ajuw", DMAP_UINT, 0, "com.apple.itunes.store.use-work-name-as-display-name" },
  122. { "amvc", DMAP_UINT, 0, "daap.songmovementcount" },
  123. { "amvm", DMAP_STR, 0, "daap.songmovementname" },
  124. { "amvn", DMAP_UINT, 0, "daap.songmovementnumber" },
  125. { "aply", DMAP_DICT, 0, "daap.databaseplaylists" },
  126. { "aprm", DMAP_UINT, 0, "daap.playlistrepeatmode" },
  127. { "apro", DMAP_VERS, 0, "daap.protocolversion" },
  128. { "apsm", DMAP_UINT, 0, "daap.playlistshufflemode" },
  129. { "apso", DMAP_DICT, 0, "daap.playlistsongs" },
  130. { "arif", DMAP_DICT, 0, "daap.resolveinfo" },
  131. { "arsv", DMAP_DICT, 0, "daap.resolve" },
  132. { "asaa", DMAP_STR, 0, "daap.songalbumartist" },
  133. { "asac", DMAP_UINT, 0, "daap.songartworkcount" },
  134. { "asai", DMAP_UINT, 0, "daap.songalbumid" },
  135. { "asal", DMAP_STR, 0, "daap.songalbum" },
  136. { "asar", DMAP_STR, 0, "daap.songartist" },
  137. { "asas", DMAP_UINT, 0, "daap.songalbumuserratingstatus" },
  138. { "asbk", DMAP_UINT, 0, "daap.bookmarkable" },
  139. { "asbo", DMAP_UINT, 0, "daap.songbookmark" },
  140. { "asbr", DMAP_UINT, 0, "daap.songbitrate" },
  141. { "asbt", DMAP_UINT, 0, "daap.songbeatsperminute" },
  142. { "ascd", DMAP_UINT, 0, "daap.songcodectype" },
  143. { "ascm", DMAP_STR, 0, "daap.songcomment" },
  144. { "ascn", DMAP_STR, 0, "daap.songcontentdescription" },
  145. { "asco", DMAP_UINT, 0, "daap.songcompilation" },
  146. { "ascp", DMAP_STR, 0, "daap.songcomposer" },
  147. { "ascr", DMAP_UINT, 0, "daap.songcontentrating" },
  148. { "ascs", DMAP_UINT, 0, "daap.songcodecsubtype" },
  149. { "asct", DMAP_STR, 0, "daap.songcategory" },
  150. { "asda", DMAP_DATE, 0, "daap.songdateadded" },
  151. { "asdb", DMAP_UINT, 0, "daap.songdisabled" },
  152. { "asdc", DMAP_UINT, 0, "daap.songdisccount" },
  153. { "asdk", DMAP_UINT, 0, "daap.songdatakind" },
  154. { "asdm", DMAP_DATE, 0, "daap.songdatemodified" },
  155. { "asdn", DMAP_UINT, 0, "daap.songdiscnumber" },
  156. { "asdp", DMAP_DATE, 0, "daap.songdatepurchased" },
  157. { "asdr", DMAP_DATE, 0, "daap.songdatereleased" },
  158. { "asdt", DMAP_STR, 0, "daap.songdescription" },
  159. { "ased", DMAP_UINT, 0, "daap.songextradata" },
  160. { "aseq", DMAP_STR, 0, "daap.songeqpreset" },
  161. { "ases", DMAP_UINT, 0, "daap.songexcludefromshuffle" },
  162. { "asfm", DMAP_STR, 0, "daap.songformat" },
  163. { "asgn", DMAP_STR, 0, "daap.songgenre" },
  164. { "asgp", DMAP_UINT, 0, "daap.songgapless" },
  165. { "asgr", DMAP_UINT, 0, "daap.supportsgroups" },
  166. { "ashp", DMAP_UINT, 0, "daap.songhasbeenplayed" },
  167. { "askd", DMAP_DATE, 0, "daap.songlastskipdate" },
  168. { "askp", DMAP_UINT, 0, "daap.songuserskipcount" },
  169. { "asky", DMAP_STR, 0, "daap.songkeywords" },
  170. { "aslc", DMAP_STR, 0, "daap.songlongcontentdescription" },
  171. { "aslr", DMAP_UINT, 0, "daap.songalbumuserrating" },
  172. { "asls", DMAP_UINT, 0, "daap.songlongsize" },
  173. { "aspc", DMAP_UINT, 0, "daap.songuserplaycount" },
  174. { "aspl", DMAP_DATE, 0, "daap.songdateplayed" },
  175. { "aspu", DMAP_STR, 0, "daap.songpodcasturl" },
  176. { "asri", DMAP_UINT, 0, "daap.songartistid" },
  177. { "asrs", DMAP_UINT, 0, "daap.songuserratingstatus" },
  178. { "asrv", DMAP_INT, 0, "daap.songrelativevolume" },
  179. { "assa", DMAP_STR, 0, "daap.sortartist" },
  180. { "assc", DMAP_STR, 0, "daap.sortcomposer" },
  181. { "assl", DMAP_STR, 0, "daap.sortalbumartist" },
  182. { "assn", DMAP_STR, 0, "daap.sortname" },
  183. { "assp", DMAP_UINT, 0, "daap.songstoptime" },
  184. { "assr", DMAP_UINT, 0, "daap.songsamplerate" },
  185. { "asss", DMAP_STR, 0, "daap.sortseriesname" },
  186. { "asst", DMAP_UINT, 0, "daap.songstarttime" },
  187. { "assu", DMAP_STR, 0, "daap.sortalbum" },
  188. { "assz", DMAP_UINT, 0, "daap.songsize" },
  189. { "astc", DMAP_UINT, 0, "daap.songtrackcount" },
  190. { "astm", DMAP_UINT, 0, "daap.songtime" },
  191. { "astn", DMAP_UINT, 0, "daap.songtracknumber" },
  192. { "asul", DMAP_STR, 0, "daap.songdataurl" },
  193. { "asur", DMAP_UINT, 0, "daap.songuserrating" },
  194. { "asvc", DMAP_UINT, 0, "daap.songprimaryvideocodec" },
  195. { "asyr", DMAP_UINT, 0, "daap.songyear" },
  196. { "ated", DMAP_UINT, 0, "daap.supportsextradata" },
  197. { "avdb", DMAP_DICT, 0, "daap.serverdatabases" },
  198. { "awrk", DMAP_STR, 0, "daap.songwork" },
  199. { "caar", DMAP_UINT, 0, "dacp.availablerepeatstates" },
  200. { "caas", DMAP_UINT, 0, "dacp.availableshufflestates" },
  201. { "caci", DMAP_DICT, 0, "caci" },
  202. { "cafe", DMAP_UINT, 0, "dacp.fullscreenenabled" },
  203. { "cafs", DMAP_UINT, 0, "dacp.fullscreen" },
  204. { "caia", DMAP_UINT, 0, "dacp.isactive" },
  205. { "cana", DMAP_STR, 0, "dacp.nowplayingartist" },
  206. { "cang", DMAP_STR, 0, "dacp.nowplayinggenre" },
  207. { "canl", DMAP_STR, 0, "dacp.nowplayingalbum" },
  208. { "cann", DMAP_STR, 0, "dacp.nowplayingname" },
  209. { "canp", DMAP_UINT, 0, "dacp.nowplayingids" },
  210. { "cant", DMAP_UINT, 0, "dacp.nowplayingtime" },
  211. { "capr", DMAP_VERS, 0, "dacp.protocolversion" },
  212. { "caps", DMAP_UINT, 0, "dacp.playerstate" },
  213. { "carp", DMAP_UINT, 0, "dacp.repeatstate" },
  214. { "cash", DMAP_UINT, 0, "dacp.shufflestate" },
  215. { "casp", DMAP_DICT, 0, "dacp.speakers" },
  216. { "cast", DMAP_UINT, 0, "dacp.songtime" },
  217. { "cavc", DMAP_UINT, 0, "dacp.volumecontrollable" },
  218. { "cave", DMAP_UINT, 0, "dacp.visualizerenabled" },
  219. { "cavs", DMAP_UINT, 0, "dacp.visualizer" },
  220. { "ceJC", DMAP_UINT, 0, "com.apple.itunes.jukebox-client-vote" },
  221. { "ceJI", DMAP_UINT, 0, "com.apple.itunes.jukebox-current" },
  222. { "ceJS", DMAP_UINT, 0, "com.apple.itunes.jukebox-score" },
  223. { "ceJV", DMAP_UINT, 0, "com.apple.itunes.jukebox-vote" },
  224. { "ceQR", DMAP_DICT, 0, "com.apple.itunes.playqueue-contents-response" },
  225. { "ceQa", DMAP_STR, 0, "com.apple.itunes.playqueue-album" },
  226. { "ceQg", DMAP_STR, 0, "com.apple.itunes.playqueue-genre" },
  227. { "ceQn", DMAP_STR, 0, "com.apple.itunes.playqueue-name" },
  228. { "ceQr", DMAP_STR, 0, "com.apple.itunes.playqueue-artist" },
  229. { "cmgt", DMAP_DICT, 0, "dmcp.getpropertyresponse" },
  230. { "cmmk", DMAP_UINT, 0, "dmcp.mediakind" },
  231. { "cmpr", DMAP_VERS, 0, "dmcp.protocolversion" },
  232. { "cmsr", DMAP_UINT, 0, "dmcp.serverrevision" },
  233. { "cmst", DMAP_DICT, 0, "dmcp.playstatus" },
  234. { "cmvo", DMAP_UINT, 0, "dmcp.volume" },
  235. { "f\215ch", DMAP_UINT, 0, "dmap.haschildcontainers" },
  236. { "ipsa", DMAP_DICT, 0, "dpap.iphotoslideshowadvancedoptions" },
  237. { "ipsl", DMAP_DICT, 0, "dpap.iphotoslideshowoptions" },
  238. { "mbcl", DMAP_DICT, 0, "dmap.bag" },
  239. { "mccr", DMAP_DICT, 0, "dmap.contentcodesresponse" },
  240. { "mcna", DMAP_STR, 0, "dmap.contentcodesname" },
  241. { "mcnm", DMAP_UINT, 0, "dmap.contentcodesnumber" },
  242. { "mcon", DMAP_DICT, 0, "dmap.container" },
  243. { "mctc", DMAP_UINT, 0, "dmap.containercount" },
  244. { "mcti", DMAP_UINT, 0, "dmap.containeritemid" },
  245. { "mcty", DMAP_UINT, 0, "dmap.contentcodestype" },
  246. { "mdbk", DMAP_UINT, 0, "dmap.databasekind" },
  247. { "mdcl", DMAP_DICT, 0, "dmap.dictionary" },
  248. { "mdst", DMAP_UINT, 0, "dmap.downloadstatus" },
  249. { "meds", DMAP_UINT, 0, "dmap.editcommandssupported" },
  250. { "meia", DMAP_UINT, 0, "dmap.itemdateadded" },
  251. { "meip", DMAP_UINT, 0, "dmap.itemdateplayed" },
  252. { "mext", DMAP_UINT, 0, "dmap.objectextradata" },
  253. #endif
  254. { "miid", DMAP_UINT, 0, "dmap.itemid" },
  255. { "mikd", DMAP_UINT, 0, "dmap.itemkind" },
  256. { "mimc", DMAP_UINT, 0, "dmap.itemcount" },
  257. { "minm", DMAP_STR, 0, "dmap.itemname" },
  258. #ifdef DMAP_FULL
  259. { "mlcl", DMAP_DICT, DMAP_DICT, "dmap.listing" },
  260. { "mlid", DMAP_UINT, 0, "dmap.sessionid" },
  261. { "mlit", DMAP_ITEM, 0, "dmap.listingitem" },
  262. { "mlog", DMAP_DICT, 0, "dmap.loginresponse" },
  263. { "mpco", DMAP_UINT, 0, "dmap.parentcontainerid" },
  264. { "mper", DMAP_UINT, 0, "dmap.persistentid" },
  265. { "mpro", DMAP_VERS, 0, "dmap.protocolversion" },
  266. { "mrco", DMAP_UINT, 0, "dmap.returnedcount" },
  267. { "mrpr", DMAP_UINT, 0, "dmap.remotepersistentid" },
  268. { "msal", DMAP_UINT, 0, "dmap.supportsautologout" },
  269. { "msas", DMAP_UINT, 0, "dmap.authenticationschemes" },
  270. { "msau", DMAP_UINT, 0, "dmap.authenticationmethod" },
  271. { "msbr", DMAP_UINT, 0, "dmap.supportsbrowse" },
  272. { "msdc", DMAP_UINT, 0, "dmap.databasescount" },
  273. { "msex", DMAP_UINT, 0, "dmap.supportsextensions" },
  274. { "msix", DMAP_UINT, 0, "dmap.supportsindex" },
  275. { "mslr", DMAP_UINT, 0, "dmap.loginrequired" },
  276. { "msma", DMAP_UINT, 0, "dmap.machineaddress" },
  277. { "msml", DMAP_DICT, 0, "msml" },
  278. { "mspi", DMAP_UINT, 0, "dmap.supportspersistentids" },
  279. { "msqy", DMAP_UINT, 0, "dmap.supportsquery" },
  280. { "msrs", DMAP_UINT, 0, "dmap.supportsresolve" },
  281. { "msrv", DMAP_DICT, 0, "dmap.serverinforesponse" },
  282. { "mstc", DMAP_DATE, 0, "dmap.utctime" },
  283. { "mstm", DMAP_UINT, 0, "dmap.timeoutinterval" },
  284. { "msto", DMAP_INT, 0, "dmap.utcoffset" },
  285. { "msts", DMAP_STR, 0, "dmap.statusstring" },
  286. { "mstt", DMAP_UINT, 0, "dmap.status" },
  287. { "msup", DMAP_UINT, 0, "dmap.supportsupdate" },
  288. { "mtco", DMAP_UINT, 0, "dmap.specifiedtotalcount" },
  289. { "mudl", DMAP_DICT, 0, "dmap.deletedidlisting" },
  290. { "mupd", DMAP_DICT, 0, "dmap.updateresponse" },
  291. { "musr", DMAP_UINT, 0, "dmap.serverrevision" },
  292. { "muty", DMAP_UINT, 0, "dmap.updatetype" },
  293. { "pasp", DMAP_STR, 0, "dpap.aspectratio" },
  294. { "pcmt", DMAP_STR, 0, "dpap.imagecomments" },
  295. { "peak", DMAP_UINT, 0, "com.apple.itunes.photos.album-kind" },
  296. { "peed", DMAP_DATE, 0, "com.apple.itunes.photos.exposure-date" },
  297. { "pefc", DMAP_DICT, 0, "com.apple.itunes.photos.faces" },
  298. { "peki", DMAP_UINT, 0, "com.apple.itunes.photos.key-image-id" },
  299. { "pekm", DMAP_DICT, 0, "com.apple.itunes.photos.key-image" },
  300. { "pemd", DMAP_DATE, 0, "com.apple.itunes.photos.modification-date" },
  301. { "pfai", DMAP_DICT, 0, "dpap.failureids" },
  302. { "pfdt", DMAP_DICT, 0, "dpap.filedata" },
  303. { "pfmt", DMAP_STR, 0, "dpap.imageformat" },
  304. { "phgt", DMAP_UINT, 0, "dpap.imagepixelheight" },
  305. { "picd", DMAP_DATE, 0, "dpap.creationdate" },
  306. { "pifs", DMAP_UINT, 0, "dpap.imagefilesize" },
  307. { "pimf", DMAP_STR, 0, "dpap.imagefilename" },
  308. { "plsz", DMAP_UINT, 0, "dpap.imagelargefilesize" },
  309. { "ppro", DMAP_VERS, 0, "dpap.protocolversion" },
  310. { "prat", DMAP_UINT, 0, "dpap.imagerating" },
  311. { "pret", DMAP_DICT, 0, "dpap.retryids" },
  312. { "pwth", DMAP_UINT, 0, "dpap.imagepixelwidth" }
  313. #endif
  314. };
  315. static const size_t dmap_field_count = sizeof(dmap_fields) / sizeof(dmap_field);
  316. typedef int (*sort_func) (const void *, const void *);
  317. int dmap_version(void) {
  318. return DMAP_VERSION;
  319. }
  320. const char *dmap_version_string(void) {
  321. return DMAP_STRINGIFY(DMAP_VERSION_MAJOR) "."
  322. DMAP_STRINGIFY(DMAP_VERSION_MINOR) "."
  323. DMAP_STRINGIFY(DMAP_VERSION_PATCH);
  324. }
  325. static int dmap_field_sort(const dmap_field *a, const dmap_field *b) {
  326. return memcmp(a->code, b->code, 4);
  327. }
  328. static const dmap_field *dmap_field_from_code(const char *code) {
  329. dmap_field key;
  330. key.code = code;
  331. return bsearch(&key, dmap_fields, dmap_field_count, sizeof(dmap_field), (sort_func)dmap_field_sort);
  332. }
  333. const char *dmap_name_from_code(const char *code) {
  334. const dmap_field *field;
  335. if (!code)
  336. return NULL;
  337. field = dmap_field_from_code(code);
  338. return field ? field->name : NULL;
  339. }
  340. static uint16_t dmap_read_u16(const char *buf) {
  341. return (uint16_t)(((buf[0] & 0xff) << 8) | (buf[1] & 0xff));
  342. }
  343. static int16_t dmap_read_i16(const char *buf) {
  344. return (int16_t)dmap_read_u16(buf);
  345. }
  346. static uint32_t dmap_read_u32(const char *buf) {
  347. return ((uint32_t)(buf[0] & 0xff) << 24) |
  348. ((uint32_t)(buf[1] & 0xff) << 16) |
  349. ((uint32_t)(buf[2] & 0xff) << 8) |
  350. ((uint32_t)(buf[3] & 0xff));
  351. }
  352. static int32_t dmap_read_i32(const char *buf) {
  353. return (int32_t)dmap_read_u32(buf);
  354. }
  355. static uint64_t dmap_read_u64(const char *buf) {
  356. return ((uint64_t)(buf[0] & 0xff) << 56) |
  357. ((uint64_t)(buf[1] & 0xff) << 48) |
  358. ((uint64_t)(buf[2] & 0xff) << 40) |
  359. ((uint64_t)(buf[3] & 0xff) << 32) |
  360. ((uint64_t)(buf[4] & 0xff) << 24) |
  361. ((uint64_t)(buf[5] & 0xff) << 16) |
  362. ((uint64_t)(buf[6] & 0xff) << 8) |
  363. ((uint64_t)(buf[7] & 0xff));
  364. }
  365. static int64_t dmap_read_i64(const char *buf) {
  366. return (int64_t)dmap_read_u64(buf);
  367. }
  368. static int dmap_parse_internal(const dmap_settings *settings, const char *buf, size_t len, const dmap_field *parent) {
  369. const dmap_field *field;
  370. DMAP_TYPE field_type;
  371. size_t field_len;
  372. const char *field_name;
  373. const char *p = buf;
  374. const char *end = buf + len;
  375. char code[5] = {0};
  376. if (!settings || !buf)
  377. return -1;
  378. while (end - p >= 8) {
  379. memcpy(code, p, 4);
  380. field = dmap_field_from_code(code);
  381. p += 4;
  382. field_len = dmap_read_u32(p);
  383. p += 4;
  384. if (p + field_len > end)
  385. return -1;
  386. if (field) {
  387. field_type = field->type;
  388. field_name = field->name;
  389. if (field_type == DMAP_ITEM) {
  390. if (parent != NULL && parent->list_item_type) {
  391. field_type = parent->list_item_type;
  392. } else {
  393. field_type = DMAP_DICT;
  394. }
  395. }
  396. } else {
  397. /* Make a best guess of the type */
  398. field_type = DMAP_UNKNOWN;
  399. field_name = code;
  400. if (field_len >= 8) {
  401. /* Look for a four char code followed by a length within the current field */
  402. if (isalpha(p[0] & 0xff) &&
  403. isalpha(p[1] & 0xff) &&
  404. isalpha(p[2] & 0xff) &&
  405. isalpha(p[3] & 0xff)) {
  406. if (dmap_read_u32(p + 4) < field_len)
  407. field_type = DMAP_DICT;
  408. }
  409. }
  410. if (field_type == DMAP_UNKNOWN) {
  411. size_t i;
  412. int is_string = 1;
  413. for (i=0; i < field_len; i++) {
  414. if (!isprint(p[i] & 0xff)) {
  415. is_string = 0;
  416. break;
  417. }
  418. }
  419. field_type = is_string ? DMAP_STR : DMAP_UINT;
  420. }
  421. }
  422. switch (field_type) {
  423. case DMAP_UINT:
  424. /* Determine the integer's type based on its size */
  425. switch (field_len) {
  426. case 1:
  427. if (settings->on_uint32)
  428. settings->on_uint32(settings->ctx, code, field_name, (unsigned char)*p);
  429. break;
  430. case 2:
  431. if (settings->on_uint32)
  432. settings->on_uint32(settings->ctx, code, field_name, dmap_read_u16(p));
  433. break;
  434. case 4:
  435. if (settings->on_uint32)
  436. settings->on_uint32(settings->ctx, code, field_name, dmap_read_u32(p));
  437. break;
  438. case 8:
  439. if (settings->on_uint64)
  440. settings->on_uint64(settings->ctx, code, field_name, dmap_read_u64(p));
  441. break;
  442. default:
  443. if (settings->on_data)
  444. settings->on_data(settings->ctx, code, field_name, p, field_len);
  445. break;
  446. }
  447. break;
  448. case DMAP_INT:
  449. switch (field_len) {
  450. case 1:
  451. if (settings->on_int32)
  452. settings->on_int32(settings->ctx, code, field_name, *p);
  453. break;
  454. case 2:
  455. if (settings->on_int32)
  456. settings->on_int32(settings->ctx, code, field_name, dmap_read_i16(p));
  457. break;
  458. case 4:
  459. if (settings->on_int32)
  460. settings->on_int32(settings->ctx, code, field_name, dmap_read_i32(p));
  461. break;
  462. case 8:
  463. if (settings->on_int64)
  464. settings->on_int64(settings->ctx, code, field_name, dmap_read_i64(p));
  465. break;
  466. default:
  467. if (settings->on_data)
  468. settings->on_data(settings->ctx, code, field_name, p, field_len);
  469. break;
  470. }
  471. break;
  472. case DMAP_STR:
  473. if (settings->on_string)
  474. settings->on_string(settings->ctx, code, field_name, p, field_len);
  475. break;
  476. case DMAP_DATA:
  477. if (settings->on_data)
  478. settings->on_data(settings->ctx, code, field_name, p, field_len);
  479. break;
  480. case DMAP_DATE:
  481. /* Seconds since epoch */
  482. if (settings->on_date)
  483. settings->on_date(settings->ctx, code, field_name, dmap_read_u32(p));
  484. break;
  485. case DMAP_VERS:
  486. if (settings->on_string && field_len >= 4) {
  487. char version[20];
  488. sprintf(version, "%u.%u", dmap_read_u16(p), dmap_read_u16(p+2));
  489. settings->on_string(settings->ctx, code, field_name, version, strlen(version));
  490. }
  491. break;
  492. case DMAP_DICT:
  493. if (settings->on_dict_start)
  494. settings->on_dict_start(settings->ctx, code, field_name);
  495. if (dmap_parse_internal(settings, p, field_len, field) != 0)
  496. return -1;
  497. if (settings->on_dict_end)
  498. settings->on_dict_end(settings->ctx, code, field_name);
  499. break;
  500. case DMAP_ITEM:
  501. /* Unreachable: listing item types are always mapped to another type */
  502. abort();
  503. case DMAP_UNKNOWN:
  504. break;
  505. }
  506. p += field_len;
  507. }
  508. if (p != end)
  509. return -1;
  510. return 0;
  511. }
  512. int dmap_parse(const dmap_settings *settings, const char *buf, size_t len) {
  513. return dmap_parse_internal(settings, buf, len, NULL);
  514. }