PageRenderTime 40ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 1ms

/includes/getid3/module.tag.id3v2.php

https://bitbucket.org/wpforchurch/sermon-manager-for-wordpress
PHP | 3331 lines | 1440 code | 335 blank | 1556 comment | 469 complexity | 6bc7ff8a2a814a5d68d8388cc83990a0 MD5 | raw file
  1. <?php
  2. /////////////////////////////////////////////////////////////////
  3. /// getID3() by James Heinrich <info@getid3.org> //
  4. // available at http://getid3.sourceforge.net //
  5. // or http://www.getid3.org //
  6. /////////////////////////////////////////////////////////////////
  7. // See readme.txt for more details //
  8. /////////////////////////////////////////////////////////////////
  9. /// //
  10. // module.tag.id3v2.php //
  11. // module for analyzing ID3v2 tags //
  12. // dependencies: module.tag.id3v1.php //
  13. // ///
  14. /////////////////////////////////////////////////////////////////
  15. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
  16. class getid3_id3v2 extends getid3_handler
  17. {
  18. var $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
  19. var $StartingOffset = 0;
  20. function Analyze() {
  21. $info = &$this->getid3->info;
  22. // Overall tag structure:
  23. // +-----------------------------+
  24. // | Header (10 bytes) |
  25. // +-----------------------------+
  26. // | Extended Header |
  27. // | (variable length, OPTIONAL) |
  28. // +-----------------------------+
  29. // | Frames (variable length) |
  30. // +-----------------------------+
  31. // | Padding |
  32. // | (variable length, OPTIONAL) |
  33. // +-----------------------------+
  34. // | Footer (10 bytes, OPTIONAL) |
  35. // +-----------------------------+
  36. // Header
  37. // ID3v2/file identifier "ID3"
  38. // ID3v2 version $04 00
  39. // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x)
  40. // ID3v2 size 4 * %0xxxxxxx
  41. // shortcuts
  42. $info['id3v2']['header'] = true;
  43. $thisfile_id3v2 = &$info['id3v2'];
  44. $thisfile_id3v2['flags'] = array();
  45. $thisfile_id3v2_flags = &$thisfile_id3v2['flags'];
  46. fseek($this->getid3->fp, $this->StartingOffset, SEEK_SET);
  47. $header = fread($this->getid3->fp, 10);
  48. if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
  49. $thisfile_id3v2['majorversion'] = ord($header{3});
  50. $thisfile_id3v2['minorversion'] = ord($header{4});
  51. // shortcut
  52. $id3v2_majorversion = &$thisfile_id3v2['majorversion'];
  53. } else {
  54. unset($info['id3v2']);
  55. return false;
  56. }
  57. if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
  58. $info['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion'];
  59. return false;
  60. }
  61. $id3_flags = ord($header{5});
  62. switch ($id3v2_majorversion) {
  63. case 2:
  64. // %ab000000 in v2.2
  65. $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
  66. $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression
  67. break;
  68. case 3:
  69. // %abc00000 in v2.3
  70. $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
  71. $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
  72. $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
  73. break;
  74. case 4:
  75. // %abcd0000 in v2.4
  76. $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
  77. $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
  78. $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
  79. $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present
  80. break;
  81. }
  82. $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
  83. $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset;
  84. $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
  85. // create 'encoding' key - used by getid3::HandleAllTags()
  86. // in ID3v2 every field can have it's own encoding type
  87. // so force everything to UTF-8 so it can be handled consistantly
  88. $thisfile_id3v2['encoding'] = 'UTF-8';
  89. // Frames
  90. // All ID3v2 frames consists of one frame header followed by one or more
  91. // fields containing the actual information. The header is always 10
  92. // bytes and laid out as follows:
  93. //
  94. // Frame ID $xx xx xx xx (four characters)
  95. // Size 4 * %0xxxxxxx
  96. // Flags $xx xx
  97. $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
  98. if (!empty($thisfile_id3v2['exthead']['length'])) {
  99. $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4);
  100. }
  101. if (!empty($thisfile_id3v2_flags['isfooter'])) {
  102. $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
  103. }
  104. if ($sizeofframes > 0) {
  105. $framedata = fread($this->getid3->fp, $sizeofframes); // read all frames from file into $framedata variable
  106. // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
  107. if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) {
  108. $framedata = $this->DeUnsynchronise($framedata);
  109. }
  110. // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
  111. // of on tag level, making it easier to skip frames, increasing the streamability
  112. // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
  113. // there exists an unsynchronised frame, while the new unsynchronisation flag in
  114. // the frame header [S:4.1.2] indicates unsynchronisation.
  115. //$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
  116. $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
  117. // Extended Header
  118. if (!empty($thisfile_id3v2_flags['exthead'])) {
  119. $extended_header_offset = 0;
  120. if ($id3v2_majorversion == 3) {
  121. // v2.3 definition:
  122. //Extended header size $xx xx xx xx // 32-bit integer
  123. //Extended Flags $xx xx
  124. // %x0000000 %00000000 // v2.3
  125. // x - CRC data present
  126. //Size of padding $xx xx xx xx
  127. $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0);
  128. $extended_header_offset += 4;
  129. $thisfile_id3v2['exthead']['flag_bytes'] = 2;
  130. $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
  131. $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
  132. $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000);
  133. $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
  134. $extended_header_offset += 4;
  135. if ($thisfile_id3v2['exthead']['flags']['crc']) {
  136. $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
  137. $extended_header_offset += 4;
  138. }
  139. $extended_header_offset += $thisfile_id3v2['exthead']['padding_size'];
  140. } elseif ($id3v2_majorversion == 4) {
  141. // v2.4 definition:
  142. //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer
  143. //Number of flag bytes $01
  144. //Extended Flags $xx
  145. // %0bcd0000 // v2.4
  146. // b - Tag is an update
  147. // Flag data length $00
  148. // c - CRC data present
  149. // Flag data length $05
  150. // Total frame CRC 5 * %0xxxxxxx
  151. // d - Tag restrictions
  152. // Flag data length $01
  153. $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true);
  154. $extended_header_offset += 4;
  155. $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1
  156. $extended_header_offset += 1;
  157. $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
  158. $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
  159. $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40);
  160. $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20);
  161. $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10);
  162. if ($thisfile_id3v2['exthead']['flags']['update']) {
  163. $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0
  164. $extended_header_offset += 1;
  165. }
  166. if ($thisfile_id3v2['exthead']['flags']['crc']) {
  167. $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5
  168. $extended_header_offset += 1;
  169. $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false);
  170. $extended_header_offset += $ext_header_chunk_length;
  171. }
  172. if ($thisfile_id3v2['exthead']['flags']['restrictions']) {
  173. $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1
  174. $extended_header_offset += 1;
  175. // %ppqrrstt
  176. $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1));
  177. $extended_header_offset += 1;
  178. $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions
  179. $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions
  180. $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions
  181. $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions
  182. $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions
  183. $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']);
  184. $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']);
  185. $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']);
  186. $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']);
  187. $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']);
  188. }
  189. if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) {
  190. $info['warning'][] = 'ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')';
  191. }
  192. }
  193. $framedataoffset += $extended_header_offset;
  194. $framedata = substr($framedata, $extended_header_offset);
  195. } // end extended header
  196. while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
  197. if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
  198. // insufficient room left in ID3v2 header for actual data - must be padding
  199. $thisfile_id3v2['padding']['start'] = $framedataoffset;
  200. $thisfile_id3v2['padding']['length'] = strlen($framedata);
  201. $thisfile_id3v2['padding']['valid'] = true;
  202. for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
  203. if ($framedata{$i} != "\x00") {
  204. $thisfile_id3v2['padding']['valid'] = false;
  205. $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
  206. $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
  207. break;
  208. }
  209. }
  210. break; // skip rest of ID3v2 header
  211. }
  212. if ($id3v2_majorversion == 2) {
  213. // Frame ID $xx xx xx (three characters)
  214. // Size $xx xx xx (24-bit integer)
  215. // Flags $xx xx
  216. $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header
  217. $framedata = substr($framedata, 6); // and leave the rest in $framedata
  218. $frame_name = substr($frame_header, 0, 3);
  219. $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0);
  220. $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs
  221. } elseif ($id3v2_majorversion > 2) {
  222. // Frame ID $xx xx xx xx (four characters)
  223. // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+)
  224. // Flags $xx xx
  225. $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header
  226. $framedata = substr($framedata, 10); // and leave the rest in $framedata
  227. $frame_name = substr($frame_header, 0, 4);
  228. if ($id3v2_majorversion == 3) {
  229. $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
  230. } else { // ID3v2.4+
  231. $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value)
  232. }
  233. if ($frame_size < (strlen($framedata) + 4)) {
  234. $nextFrameID = substr($framedata, $frame_size, 4);
  235. if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) {
  236. // next frame is OK
  237. } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
  238. // MP3ext known broken frames - "ok" for the purposes of this test
  239. } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
  240. $info['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3';
  241. $id3v2_majorversion = 3;
  242. $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
  243. }
  244. }
  245. $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2));
  246. }
  247. if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) {
  248. // padding encountered
  249. $thisfile_id3v2['padding']['start'] = $framedataoffset;
  250. $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata);
  251. $thisfile_id3v2['padding']['valid'] = true;
  252. $len = strlen($framedata);
  253. for ($i = 0; $i < $len; $i++) {
  254. if ($framedata{$i} != "\x00") {
  255. $thisfile_id3v2['padding']['valid'] = false;
  256. $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
  257. $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
  258. break;
  259. }
  260. }
  261. break; // skip rest of ID3v2 header
  262. }
  263. if ($frame_name == 'COM ') {
  264. $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]';
  265. $frame_name = 'COMM';
  266. }
  267. if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
  268. unset($parsedFrame);
  269. $parsedFrame['frame_name'] = $frame_name;
  270. $parsedFrame['frame_flags_raw'] = $frame_flags;
  271. $parsedFrame['data'] = substr($framedata, 0, $frame_size);
  272. $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size);
  273. $parsedFrame['dataoffset'] = $framedataoffset;
  274. $this->ParseID3v2Frame($parsedFrame);
  275. $thisfile_id3v2[$frame_name][] = $parsedFrame;
  276. $framedata = substr($framedata, $frame_size);
  277. } else { // invalid frame length or FrameID
  278. if ($frame_size <= strlen($framedata)) {
  279. if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) {
  280. // next frame is valid, just skip the current frame
  281. $framedata = substr($framedata, $frame_size);
  282. $info['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.';
  283. } else {
  284. // next frame is invalid too, abort processing
  285. //unset($framedata);
  286. $framedata = null;
  287. $info['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.';
  288. }
  289. } elseif ($frame_size == strlen($framedata)) {
  290. // this is the last frame, just skip
  291. $info['warning'][] = 'This was the last ID3v2 frame.';
  292. } else {
  293. // next frame is invalid too, abort processing
  294. //unset($framedata);
  295. $framedata = null;
  296. $info['warning'][] = 'Invalid ID3v2 frame size, aborting.';
  297. }
  298. if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) {
  299. switch ($frame_name) {
  300. case "\x00\x00".'MP':
  301. case "\x00".'MP3':
  302. case ' MP3':
  303. case 'MP3e':
  304. case "\x00".'MP':
  305. case ' MP':
  306. case 'MP3':
  307. $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]';
  308. break;
  309. default:
  310. $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).';
  311. break;
  312. }
  313. } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) {
  314. $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).';
  315. } else {
  316. $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).';
  317. }
  318. }
  319. $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion));
  320. }
  321. }
  322. // Footer
  323. // The footer is a copy of the header, but with a different identifier.
  324. // ID3v2 identifier "3DI"
  325. // ID3v2 version $04 00
  326. // ID3v2 flags %abcd0000
  327. // ID3v2 size 4 * %0xxxxxxx
  328. if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
  329. $footer = fread($this->getid3->fp, 10);
  330. if (substr($footer, 0, 3) == '3DI') {
  331. $thisfile_id3v2['footer'] = true;
  332. $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
  333. $thisfile_id3v2['minorversion_footer'] = ord($footer{4});
  334. }
  335. if ($thisfile_id3v2['majorversion_footer'] <= 4) {
  336. $id3_flags = ord(substr($footer{5}));
  337. $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80);
  338. $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40);
  339. $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20);
  340. $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10);
  341. $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1);
  342. }
  343. } // end footer
  344. if (isset($thisfile_id3v2['comments']['genre'])) {
  345. foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
  346. unset($thisfile_id3v2['comments']['genre'][$key]);
  347. $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value)));
  348. }
  349. }
  350. if (isset($thisfile_id3v2['comments']['track'])) {
  351. foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
  352. if (strstr($value, '/')) {
  353. list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
  354. }
  355. }
  356. }
  357. if (!isset($thisfile_id3v2['comments']['year']) && !empty($thisfile_id3v2['comments']['recording_time'][0]) && preg_match('#^([0-9]{4})#', trim($thisfile_id3v2['comments']['recording_time'][0]), $matches)) {
  358. $thisfile_id3v2['comments']['year'] = array($matches[1]);
  359. }
  360. if (!empty($thisfile_id3v2['TXXX'])) {
  361. // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames
  362. foreach ($thisfile_id3v2['TXXX'] as $txxx_array) {
  363. switch ($txxx_array['description']) {
  364. case 'replaygain_track_gain':
  365. if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) {
  366. $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
  367. }
  368. break;
  369. case 'replaygain_track_peak':
  370. if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) {
  371. $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']);
  372. }
  373. break;
  374. case 'replaygain_album_gain':
  375. if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) {
  376. $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
  377. }
  378. break;
  379. }
  380. }
  381. }
  382. // Set avdataoffset
  383. $info['avdataoffset'] = $thisfile_id3v2['headerlength'];
  384. if (isset($thisfile_id3v2['footer'])) {
  385. $info['avdataoffset'] += 10;
  386. }
  387. return true;
  388. }
  389. function ParseID3v2GenreString($genrestring) {
  390. // Parse genres into arrays of genreName and genreID
  391. // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
  392. // ID3v2.4.x: '21' $00 'Eurodisco' $00
  393. $clean_genres = array();
  394. if (strpos($genrestring, "\x00") === false) {
  395. $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
  396. }
  397. $genre_elements = explode("\x00", $genrestring);
  398. foreach ($genre_elements as $element) {
  399. $element = trim($element);
  400. if ($element) {
  401. if (preg_match('#^[0-9]{1,3}#', $element)) {
  402. $clean_genres[] = getid3_id3v1::LookupGenreName($element);
  403. } else {
  404. $clean_genres[] = str_replace('((', '(', $element);
  405. }
  406. }
  407. }
  408. return $clean_genres;
  409. }
  410. function ParseID3v2Frame(&$parsedFrame) {
  411. // shortcuts
  412. $info = &$this->getid3->info;
  413. $id3v2_majorversion = $info['id3v2']['majorversion'];
  414. $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
  415. if (empty($parsedFrame['framenamelong'])) {
  416. unset($parsedFrame['framenamelong']);
  417. }
  418. $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
  419. if (empty($parsedFrame['framenameshort'])) {
  420. unset($parsedFrame['framenameshort']);
  421. }
  422. if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard
  423. if ($id3v2_majorversion == 3) {
  424. // Frame Header Flags
  425. // %abc00000 %ijk00000
  426. $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation
  427. $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation
  428. $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only
  429. $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression
  430. $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption
  431. $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity
  432. } elseif ($id3v2_majorversion == 4) {
  433. // Frame Header Flags
  434. // %0abc0000 %0h00kmnp
  435. $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation
  436. $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation
  437. $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only
  438. $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity
  439. $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression
  440. $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption
  441. $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation
  442. $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator
  443. // Frame-level de-unsynchronisation - ID3v2.4
  444. if ($parsedFrame['flags']['Unsynchronisation']) {
  445. $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
  446. }
  447. if ($parsedFrame['flags']['DataLengthIndicator']) {
  448. $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1);
  449. $parsedFrame['data'] = substr($parsedFrame['data'], 4);
  450. }
  451. }
  452. // Frame-level de-compression
  453. if ($parsedFrame['flags']['compression']) {
  454. $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
  455. if (!function_exists('gzuncompress')) {
  456. $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"';
  457. } else {
  458. if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
  459. //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) {
  460. $parsedFrame['data'] = $decompresseddata;
  461. unset($decompresseddata);
  462. } else {
  463. $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"';
  464. }
  465. }
  466. }
  467. }
  468. if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
  469. if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
  470. $info['warning'][] = 'ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data';
  471. }
  472. }
  473. if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) {
  474. $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion';
  475. switch ($parsedFrame['frame_name']) {
  476. case 'WCOM':
  477. $warning .= ' (this is known to happen with files tagged by RioPort)';
  478. break;
  479. default:
  480. break;
  481. }
  482. $info['warning'][] = $warning;
  483. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier
  484. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier
  485. // There may be more than one 'UFID' frame in a tag,
  486. // but only one with the same 'Owner identifier'.
  487. // <Header for 'Unique file identifier', ID: 'UFID'>
  488. // Owner identifier <text string> $00
  489. // Identifier <up to 64 bytes binary data>
  490. $exploded = explode("\x00", $parsedFrame['data'], 2);
  491. $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : '');
  492. $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : '');
  493. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame
  494. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame
  495. // There may be more than one 'TXXX' frame in each tag,
  496. // but only one with the same description.
  497. // <Header for 'User defined text information frame', ID: 'TXXX'>
  498. // Text encoding $xx
  499. // Description <text string according to encoding> $00 (00)
  500. // Value <text string according to encoding>
  501. $frame_offset = 0;
  502. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  503. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  504. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  505. }
  506. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  507. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  508. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  509. }
  510. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  511. if (ord($frame_description) === 0) {
  512. $frame_description = '';
  513. }
  514. $parsedFrame['encodingid'] = $frame_textencoding;
  515. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  516. $parsedFrame['description'] = $frame_description;
  517. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
  518. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  519. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
  520. }
  521. //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
  522. } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
  523. // There may only be one text information frame of its kind in an tag.
  524. // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
  525. // excluding 'TXXX' described in 4.2.6.>
  526. // Text encoding $xx
  527. // Information <text string(s) according to encoding>
  528. $frame_offset = 0;
  529. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  530. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  531. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  532. }
  533. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  534. $parsedFrame['encodingid'] = $frame_textencoding;
  535. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  536. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  537. $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  538. $string = rtrim($string, "\x00"); // remove possible terminating null (put by encoding id or software bug)
  539. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
  540. unset($string);
  541. }
  542. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame
  543. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame
  544. // There may be more than one 'WXXX' frame in each tag,
  545. // but only one with the same description
  546. // <Header for 'User defined URL link frame', ID: 'WXXX'>
  547. // Text encoding $xx
  548. // Description <text string according to encoding> $00 (00)
  549. // URL <text string>
  550. $frame_offset = 0;
  551. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  552. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  553. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  554. }
  555. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  556. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  557. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  558. }
  559. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  560. if (ord($frame_description) === 0) {
  561. $frame_description = '';
  562. }
  563. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
  564. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
  565. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  566. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  567. }
  568. if ($frame_terminatorpos) {
  569. // there are null bytes after the data - this is not according to spec
  570. // only use data up to first null byte
  571. $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
  572. } else {
  573. // no null bytes following data, just use all data
  574. $frame_urldata = (string) $parsedFrame['data'];
  575. }
  576. $parsedFrame['encodingid'] = $frame_textencoding;
  577. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  578. $parsedFrame['url'] = $frame_urldata;
  579. $parsedFrame['description'] = $frame_description;
  580. if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
  581. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
  582. }
  583. unset($parsedFrame['data']);
  584. } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
  585. // There may only be one URL link frame of its kind in a tag,
  586. // except when stated otherwise in the frame description
  587. // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
  588. // described in 4.3.2.>
  589. // URL <text string>
  590. $parsedFrame['url'] = trim($parsedFrame['data']);
  591. if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
  592. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
  593. }
  594. unset($parsedFrame['data']);
  595. } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only)
  596. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only)
  597. // There may only be one 'IPL' frame in each tag
  598. // <Header for 'User defined URL link frame', ID: 'IPL'>
  599. // Text encoding $xx
  600. // People list strings <textstrings>
  601. $frame_offset = 0;
  602. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  603. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  604. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  605. }
  606. $parsedFrame['encodingid'] = $frame_textencoding;
  607. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
  608. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  609. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  610. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  611. }
  612. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier
  613. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier
  614. // There may only be one 'MCDI' frame in each tag
  615. // <Header for 'Music CD identifier', ID: 'MCDI'>
  616. // CD TOC <binary data>
  617. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  618. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
  619. }
  620. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes
  621. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes
  622. // There may only be one 'ETCO' frame in each tag
  623. // <Header for 'Event timing codes', ID: 'ETCO'>
  624. // Time stamp format $xx
  625. // Where time stamp format is:
  626. // $01 (32-bit value) MPEG frames from beginning of file
  627. // $02 (32-bit value) milliseconds from beginning of file
  628. // Followed by a list of key events in the following format:
  629. // Type of event $xx
  630. // Time stamp $xx (xx ...)
  631. // The 'Time stamp' is set to zero if directly at the beginning of the sound
  632. // or after the previous event. All events MUST be sorted in chronological order.
  633. $frame_offset = 0;
  634. $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  635. while ($frame_offset < strlen($parsedFrame['data'])) {
  636. $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
  637. $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
  638. $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  639. $frame_offset += 4;
  640. }
  641. unset($parsedFrame['data']);
  642. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table
  643. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table
  644. // There may only be one 'MLLT' frame in each tag
  645. // <Header for 'Location lookup table', ID: 'MLLT'>
  646. // MPEG frames between reference $xx xx
  647. // Bytes between reference $xx xx xx
  648. // Milliseconds between reference $xx xx xx
  649. // Bits for bytes deviation $xx
  650. // Bits for milliseconds dev. $xx
  651. // Then for every reference the following data is included;
  652. // Deviation in bytes %xxx....
  653. // Deviation in milliseconds %xxx....
  654. $frame_offset = 0;
  655. $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
  656. $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
  657. $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
  658. $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
  659. $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
  660. $parsedFrame['data'] = substr($parsedFrame['data'], 10);
  661. while ($frame_offset < strlen($parsedFrame['data'])) {
  662. $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
  663. }
  664. $reference_counter = 0;
  665. while (strlen($deviationbitstream) > 0) {
  666. $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
  667. $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
  668. $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
  669. $reference_counter++;
  670. }
  671. unset($parsedFrame['data']);
  672. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes
  673. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes
  674. // There may only be one 'SYTC' frame in each tag
  675. // <Header for 'Synchronised tempo codes', ID: 'SYTC'>
  676. // Time stamp format $xx
  677. // Tempo data <binary data>
  678. // Where time stamp format is:
  679. // $01 (32-bit value) MPEG frames from beginning of file
  680. // $02 (32-bit value) milliseconds from beginning of file
  681. $frame_offset = 0;
  682. $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  683. $timestamp_counter = 0;
  684. while ($frame_offset < strlen($parsedFrame['data'])) {
  685. $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  686. if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
  687. $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
  688. }
  689. $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  690. $frame_offset += 4;
  691. $timestamp_counter++;
  692. }
  693. unset($parsedFrame['data']);
  694. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription
  695. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription
  696. // There may be more than one 'Unsynchronised lyrics/text transcription' frame
  697. // in each tag, but only one with the same language and content descriptor.
  698. // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
  699. // Text encoding $xx
  700. // Language $xx xx xx
  701. // Content descriptor <text string according to encoding> $00 (00)
  702. // Lyrics/text <full text string according to encoding>
  703. $frame_offset = 0;
  704. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  705. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  706. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  707. }
  708. $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
  709. $frame_offset += 3;
  710. // ADDED BY POWERPRESS: Added the following two lines prevent PHP warnings if offset larger than string length
  711. if( strlen($parsedFrame['data']) < $frame_offset )
  712. $frame_offset = strlen($parsedFrame['data']);
  713. // END ADDED BY POWERPRESS
  714. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  715. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  716. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  717. }
  718. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  719. if (ord($frame_description) === 0) {
  720. $frame_description = '';
  721. }
  722. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
  723. $parsedFrame['encodingid'] = $frame_textencoding;
  724. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  725. $parsedFrame['data'] = $parsedFrame['data'];
  726. $parsedFrame['language'] = $frame_language;
  727. $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
  728. $parsedFrame['description'] = $frame_description;
  729. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  730. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  731. }
  732. unset($parsedFrame['data']);
  733. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text
  734. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text
  735. // There may be more than one 'SYLT' frame in each tag,
  736. // but only one with the same language and content descriptor.
  737. // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
  738. // Text encoding $xx
  739. // Language $xx xx xx
  740. // Time stamp format $xx
  741. // $01 (32-bit value) MPEG frames from beginning of file
  742. // $02 (32-bit value) milliseconds from beginning of file
  743. // Content type $xx
  744. // Content descriptor <text string according to encoding> $00 (00)
  745. // Terminated text to be synced (typically a syllable)
  746. // Sync identifier (terminator to above string) $00 (00)
  747. // Time stamp $xx (xx ...)
  748. $frame_offset = 0;
  749. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  750. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  751. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  752. }
  753. $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
  754. $frame_offset += 3;
  755. $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  756. $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  757. $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
  758. $parsedFrame['encodingid'] = $frame_textencoding;
  759. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  760. $parsedFrame['language'] = $frame_language;
  761. $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
  762. $timestampindex = 0;
  763. $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
  764. while (strlen($frame_remainingdata)) {
  765. $frame_offset = 0;
  766. $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding));
  767. if ($frame_terminatorpos === false) {
  768. $frame_remainingdata = '';
  769. } else {
  770. if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  771. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  772. }
  773. $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
  774. $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
  775. if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
  776. // timestamp probably omitted for first data item
  777. } else {
  778. $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
  779. $frame_remainingdata = substr($frame_remainingdata, 4);
  780. }
  781. $timestampindex++;
  782. }
  783. }
  784. unset($parsedFrame['data']);
  785. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments
  786. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments
  787. // There may be more than one comment frame in each tag,
  788. // but only one with the same language and content descriptor.
  789. // <Header for 'Comment', ID: 'COMM'>
  790. // Text encoding $xx
  791. // Language $xx xx xx
  792. // Short content descrip. <text string according to encoding> $00 (00)
  793. // The actual text <full text string according to encoding>
  794. if (strlen($parsedFrame['data']) < 5) {
  795. $info['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset'];
  796. } else {
  797. $frame_offset = 0;
  798. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  799. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  800. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  801. }
  802. $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
  803. $frame_offset += 3;
  804. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  805. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  806. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  807. }
  808. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  809. if (ord($frame_description) === 0) {
  810. $frame_description = '';
  811. }
  812. $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
  813. $parsedFrame['encodingid'] = $frame_textencoding;
  814. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  815. $parsedFrame['language'] = $frame_language;
  816. $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
  817. $parsedFrame['description'] = $frame_description;
  818. $parsedFrame['data'] = $frame_text;
  819. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  820. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  821. }
  822. }
  823. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
  824. // There may be more than one 'RVA2' frame in each tag,
  825. // but only one with the same identification string
  826. // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
  827. // Identification <text string> $00
  828. // The 'identification' string is used to identify the situation and/or
  829. // device where this adjustment should apply. The following is then
  830. // repeated for every channel:
  831. // Type of channel $xx
  832. // Volume adjustment $xx xx
  833. // Bits representing peak $xx
  834. // Peak volume $xx (xx ...)
  835. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00");
  836. $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
  837. if (ord($frame_idstring) === 0) {
  838. $frame_idstring = '';
  839. }
  840. $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
  841. $parsedFrame['description'] = $frame_idstring;
  842. $RVA2channelcounter = 0;
  843. while (strlen($frame_remainingdata) >= 5) {
  844. $frame_offset = 0;
  845. $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
  846. $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid;
  847. $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
  848. $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed
  849. $frame_offset += 2;
  850. $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
  851. if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) {
  852. $info['warning'][] = 'ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value';
  853. break;
  854. }
  855. $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8);
  856. $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
  857. $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
  858. $RVA2channelcounter++;
  859. }
  860. unset($parsedFrame['data']);
  861. } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
  862. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only)
  863. // There may only be one 'RVA' frame in each tag
  864. // <Header for 'Relative volume adjustment', ID: 'RVA'>
  865. // ID3v2.2 => Increment/decrement %000000ba
  866. // ID3v2.3 => Increment/decrement %00fedcba
  867. // Bits used for volume descr. $xx
  868. // Relative volume change, right $xx xx (xx ...) // a
  869. // Relative volume change, left $xx xx (xx ...) // b
  870. // Peak volume right $xx xx (xx ...)
  871. // Peak volume left $xx xx (xx ...)
  872. // ID3v2.3 only, optional (not present in ID3v2.2):
  873. // Relative volume change, right back $xx xx (xx ...) // c
  874. // Relative volume change, left back $xx xx (xx ...) // d
  875. // Peak volume right back $xx xx (xx ...)
  876. // Peak volume left back $xx xx (xx ...)
  877. // ID3v2.3 only, optional (not present in ID3v2.2):
  878. // Relative volume change, center $xx xx (xx ...) // e
  879. // Peak volume center $xx xx (xx ...)
  880. // ID3v2.3 only, optional (not present in ID3v2.2):
  881. // Relative volume change, bass $xx xx (xx ...) // f
  882. // Peak volume bass $xx xx (xx ...)
  883. $frame_offset = 0;
  884. $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
  885. $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
  886. $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
  887. $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  888. $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
  889. $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  890. if ($parsedFrame['incdec']['right'] === false) {
  891. $parsedFrame['volumechange']['right'] *= -1;
  892. }
  893. $frame_offset += $frame_bytesvolume;
  894. $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  895. if ($parsedFrame['incdec']['left'] === false) {
  896. $parsedFrame['volumechange']['left'] *= -1;
  897. }
  898. $frame_offset += $frame_bytesvolume;
  899. $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  900. $frame_offset += $frame_bytesvolume;
  901. $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  902. $frame_offset += $frame_bytesvolume;
  903. if ($id3v2_majorversion == 3) {
  904. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
  905. if (strlen($parsedFrame['data']) > 0) {
  906. $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
  907. $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
  908. $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  909. if ($parsedFrame['incdec']['rightrear'] === false) {
  910. $parsedFrame['volumechange']['rightrear'] *= -1;
  911. }
  912. $frame_offset += $frame_bytesvolume;
  913. $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  914. if ($parsedFrame['incdec']['leftrear'] === false) {
  915. $parsedFrame['volumechange']['leftrear'] *= -1;
  916. }
  917. $frame_offset += $frame_bytesvolume;
  918. $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  919. $frame_offset += $frame_bytesvolume;
  920. $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  921. $frame_offset += $frame_bytesvolume;
  922. }
  923. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
  924. if (strlen($parsedFrame['data']) > 0) {
  925. $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
  926. $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  927. if ($parsedFrame['incdec']['center'] === false) {
  928. $parsedFrame['volumechange']['center'] *= -1;
  929. }
  930. $frame_offset += $frame_bytesvolume;
  931. $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  932. $frame_offset += $frame_bytesvolume;
  933. }
  934. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
  935. if (strlen($parsedFrame['data']) > 0) {
  936. $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
  937. $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  938. if ($parsedFrame['incdec']['bass'] === false) {
  939. $parsedFrame['volumechange']['bass'] *= -1;
  940. }
  941. $frame_offset += $frame_bytesvolume;
  942. $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
  943. $frame_offset += $frame_bytesvolume;
  944. }
  945. }
  946. unset($parsedFrame['data']);
  947. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
  948. // There may be more than one 'EQU2' frame in each tag,
  949. // but only one with the same identification string
  950. // <Header of 'Equalisation (2)', ID: 'EQU2'>
  951. // Interpolation method $xx
  952. // $00 Band
  953. // $01 Linear
  954. // Identification <text string> $00
  955. // The following is then repeated for every adjustment point
  956. // Frequency $xx xx
  957. // Volume adjustment $xx xx
  958. $frame_offset = 0;
  959. $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  960. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  961. $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  962. if (ord($frame_idstring) === 0) {
  963. $frame_idstring = '';
  964. }
  965. $parsedFrame['description'] = $frame_idstring;
  966. $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
  967. while (strlen($frame_remainingdata)) {
  968. $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
  969. $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
  970. $frame_remainingdata = substr($frame_remainingdata, 4);
  971. }
  972. $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
  973. unset($parsedFrame['data']);
  974. } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only)
  975. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only)
  976. // There may only be one 'EQUA' frame in each tag
  977. // <Header for 'Relative volume adjustment', ID: 'EQU'>
  978. // Adjustment bits $xx
  979. // This is followed by 2 bytes + ('adjustment bits' rounded up to the
  980. // nearest byte) for every equalisation band in the following format,
  981. // giving a frequency range of 0 - 32767Hz:
  982. // Increment/decrement %x (MSB of the Frequency)
  983. // Frequency (lower 15 bits)
  984. // Adjustment $xx (xx ...)
  985. $frame_offset = 0;
  986. $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
  987. $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
  988. $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
  989. while (strlen($frame_remainingdata) > 0) {
  990. $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
  991. $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
  992. $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
  993. $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
  994. $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
  995. if ($parsedFrame[$frame_frequency]['incdec'] === false) {
  996. $parsedFrame[$frame_frequency]['adjustment'] *= -1;
  997. }
  998. $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
  999. }
  1000. unset($parsedFrame['data']);
  1001. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb
  1002. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb
  1003. // There may only be one 'RVRB' frame in each tag.
  1004. // <Header for 'Reverb', ID: 'RVRB'>
  1005. // Reverb left (ms) $xx xx
  1006. // Reverb right (ms) $xx xx
  1007. // Reverb bounces, left $xx
  1008. // Reverb bounces, right $xx
  1009. // Reverb feedback, left to left $xx
  1010. // Reverb feedback, left to right $xx
  1011. // Reverb feedback, right to right $xx
  1012. // Reverb feedback, right to left $xx
  1013. // Premix left to right $xx
  1014. // Premix right to left $xx
  1015. $frame_offset = 0;
  1016. $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1017. $frame_offset += 2;
  1018. $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1019. $frame_offset += 2;
  1020. $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1021. $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1022. $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1023. $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1024. $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1025. $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1026. $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1027. $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1028. unset($parsedFrame['data']);
  1029. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture
  1030. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture
  1031. // There may be several pictures attached to one file,
  1032. // each in their individual 'APIC' frame, but only one
  1033. // with the same content descriptor
  1034. // <Header for 'Attached picture', ID: 'APIC'>
  1035. // Text encoding $xx
  1036. // ID3v2.3+ => MIME type <text string> $00
  1037. // ID3v2.2 => Image format $xx xx xx
  1038. // Picture type $xx
  1039. // Description <text string according to encoding> $00 (00)
  1040. // Picture data <binary data>
  1041. $frame_offset = 0;
  1042. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1043. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1044. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  1045. }
  1046. if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
  1047. $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
  1048. if (strtolower($frame_imagetype) == 'ima') {
  1049. // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
  1050. // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoff�pacbell*net)
  1051. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1052. $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1053. if (ord($frame_mimetype) === 0) {
  1054. $frame_mimetype = '';
  1055. }
  1056. $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
  1057. if ($frame_imagetype == 'JPEG') {
  1058. $frame_imagetype = 'JPG';
  1059. }
  1060. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1061. } else {
  1062. $frame_offset += 3;
  1063. }
  1064. }
  1065. if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
  1066. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1067. $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1068. if (ord($frame_mimetype) === 0) {
  1069. $frame_mimetype = '';
  1070. }
  1071. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1072. }
  1073. $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1074. if ($frame_offset >= $parsedFrame['datalength']) {
  1075. $info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset);
  1076. } else {
  1077. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  1078. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  1079. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1080. }
  1081. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1082. if (ord($frame_description) === 0) {
  1083. $frame_description = '';
  1084. }
  1085. $parsedFrame['encodingid'] = $frame_textencoding;
  1086. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1087. if ($id3v2_majorversion == 2) {
  1088. $parsedFrame['imagetype'] = $frame_imagetype;
  1089. } else {
  1090. $parsedFrame['mime'] = $frame_mimetype;
  1091. }
  1092. $parsedFrame['picturetypeid'] = $frame_picturetype;
  1093. $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
  1094. $parsedFrame['description'] = $frame_description;
  1095. $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
  1096. $parsedFrame['datalength'] = strlen($parsedFrame['data']);
  1097. $parsedFrame['image_mime'] = '';
  1098. $imageinfo = array();
  1099. $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo);
  1100. if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
  1101. $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
  1102. if ($imagechunkcheck[0]) {
  1103. $parsedFrame['image_width'] = $imagechunkcheck[0];
  1104. }
  1105. if ($imagechunkcheck[1]) {
  1106. $parsedFrame['image_height'] = $imagechunkcheck[1];
  1107. }
  1108. }
  1109. do {
  1110. if ($this->inline_attachments === false) {
  1111. // skip entirely
  1112. unset($parsedFrame['data']);
  1113. break;
  1114. }
  1115. if ($this->inline_attachments === true) {
  1116. // great
  1117. } elseif (is_int($this->inline_attachments)) {
  1118. if ($this->inline_attachments < $parsedFrame['data_length']) {
  1119. // too big, skip
  1120. $info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)';
  1121. unset($parsedFrame['data']);
  1122. break;
  1123. }
  1124. } elseif (is_string($this->inline_attachments)) {
  1125. $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
  1126. if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
  1127. // cannot write, skip
  1128. $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$this->inline_attachments.'" (not writable)';
  1129. unset($parsedFrame['data']);
  1130. break;
  1131. }
  1132. }
  1133. // if we get this far, must be OK
  1134. if (is_string($this->inline_attachments)) {
  1135. $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset;
  1136. if (!file_exists($destination_filename) || is_writable($destination_filename)) {
  1137. file_put_contents($destination_filename, $parsedFrame['data']);
  1138. } else {
  1139. $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)';
  1140. }
  1141. $parsedFrame['data_filename'] = $destination_filename;
  1142. unset($parsedFrame['data']);
  1143. } else {
  1144. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  1145. if (!isset($info['id3v2']['comments']['picture'])) {
  1146. $info['id3v2']['comments']['picture'] = array();
  1147. }
  1148. $info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']);
  1149. }
  1150. }
  1151. } while (false);
  1152. }
  1153. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object
  1154. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object
  1155. // There may be more than one 'GEOB' frame in each tag,
  1156. // but only one with the same content descriptor
  1157. // <Header for 'General encapsulated object', ID: 'GEOB'>
  1158. // Text encoding $xx
  1159. // MIME type <text string> $00
  1160. // Filename <text string according to encoding> $00 (00)
  1161. // Content description <text string according to encoding> $00 (00)
  1162. // Encapsulated object <binary data>
  1163. $frame_offset = 0;
  1164. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1165. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1166. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  1167. }
  1168. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1169. $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1170. if (ord($frame_mimetype) === 0) {
  1171. $frame_mimetype = '';
  1172. }
  1173. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1174. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  1175. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  1176. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1177. }
  1178. $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1179. if (ord($frame_filename) === 0) {
  1180. $frame_filename = '';
  1181. }
  1182. $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
  1183. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  1184. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  1185. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1186. }
  1187. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1188. if (ord($frame_description) === 0) {
  1189. $frame_description = '';
  1190. }
  1191. $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
  1192. $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
  1193. $parsedFrame['encodingid'] = $frame_textencoding;
  1194. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1195. $parsedFrame['mime'] = $frame_mimetype;
  1196. $parsedFrame['filename'] = $frame_filename;
  1197. $parsedFrame['description'] = $frame_description;
  1198. unset($parsedFrame['data']);
  1199. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter
  1200. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter
  1201. // There may only be one 'PCNT' frame in each tag.
  1202. // When the counter reaches all one's, one byte is inserted in
  1203. // front of the counter thus making the counter eight bits bigger
  1204. // <Header for 'Play counter', ID: 'PCNT'>
  1205. // Counter $xx xx xx xx (xx ...)
  1206. $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
  1207. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter
  1208. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter
  1209. // There may be more than one 'POPM' frame in each tag,
  1210. // but only one with the same email address
  1211. // <Header for 'Popularimeter', ID: 'POPM'>
  1212. // Email to user <text string> $00
  1213. // Rating $xx
  1214. // Counter $xx xx xx xx (xx ...)
  1215. $frame_offset = 0;
  1216. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1217. $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1218. if (ord($frame_emailaddress) === 0) {
  1219. $frame_emailaddress = '';
  1220. }
  1221. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1222. $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1223. $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
  1224. $parsedFrame['email'] = $frame_emailaddress;
  1225. $parsedFrame['rating'] = $frame_rating;
  1226. unset($parsedFrame['data']);
  1227. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size
  1228. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size
  1229. // There may only be one 'RBUF' frame in each tag
  1230. // <Header for 'Recommended buffer size', ID: 'RBUF'>
  1231. // Buffer size $xx xx xx
  1232. // Embedded info flag %0000000x
  1233. // Offset to next tag $xx xx xx xx
  1234. $frame_offset = 0;
  1235. $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
  1236. $frame_offset += 3;
  1237. $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
  1238. $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
  1239. $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1240. unset($parsedFrame['data']);
  1241. } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only)
  1242. // There may be more than one 'CRM' frame in a tag,
  1243. // but only one with the same 'owner identifier'
  1244. // <Header for 'Encrypted meta frame', ID: 'CRM'>
  1245. // Owner identifier <textstring> $00 (00)
  1246. // Content/explanation <textstring> $00 (00)
  1247. // Encrypted datablock <binary data>
  1248. $frame_offset = 0;
  1249. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1250. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1251. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1252. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1253. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1254. if (ord($frame_description) === 0) {
  1255. $frame_description = '';
  1256. }
  1257. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1258. $parsedFrame['ownerid'] = $frame_ownerid;
  1259. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1260. $parsedFrame['description'] = $frame_description;
  1261. unset($parsedFrame['data']);
  1262. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption
  1263. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption
  1264. // There may be more than one 'AENC' frames in a tag,
  1265. // but only one with the same 'Owner identifier'
  1266. // <Header for 'Audio encryption', ID: 'AENC'>
  1267. // Owner identifier <text string> $00
  1268. // Preview start $xx xx
  1269. // Preview length $xx xx
  1270. // Encryption info <binary data>
  1271. $frame_offset = 0;
  1272. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1273. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1274. if (ord($frame_ownerid) === 0) {
  1275. $frame_ownerid == '';
  1276. }
  1277. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1278. $parsedFrame['ownerid'] = $frame_ownerid;
  1279. $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1280. $frame_offset += 2;
  1281. $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1282. $frame_offset += 2;
  1283. $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
  1284. unset($parsedFrame['data']);
  1285. } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information
  1286. (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information
  1287. // There may be more than one 'LINK' frame in a tag,
  1288. // but only one with the same contents
  1289. // <Header for 'Linked information', ID: 'LINK'>
  1290. // ID3v2.3+ => Frame identifier $xx xx xx xx
  1291. // ID3v2.2 => Frame identifier $xx xx xx
  1292. // URL <text string> $00
  1293. // ID and additional data <text string(s)>
  1294. $frame_offset = 0;
  1295. if ($id3v2_majorversion == 2) {
  1296. $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
  1297. $frame_offset += 3;
  1298. } else {
  1299. $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
  1300. $frame_offset += 4;
  1301. }
  1302. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1303. $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1304. if (ord($frame_url) === 0) {
  1305. $frame_url = '';
  1306. }
  1307. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1308. $parsedFrame['url'] = $frame_url;
  1309. $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
  1310. if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
  1311. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']);
  1312. }
  1313. unset($parsedFrame['data']);
  1314. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
  1315. // There may only be one 'POSS' frame in each tag
  1316. // <Head for 'Position synchronisation', ID: 'POSS'>
  1317. // Time stamp format $xx
  1318. // Position $xx (xx ...)
  1319. $frame_offset = 0;
  1320. $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1321. $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
  1322. unset($parsedFrame['data']);
  1323. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only)
  1324. // There may be more than one 'Terms of use' frame in a tag,
  1325. // but only one with the same 'Language'
  1326. // <Header for 'Terms of use frame', ID: 'USER'>
  1327. // Text encoding $xx
  1328. // Language $xx xx xx
  1329. // The actual text <text string according to encoding>
  1330. $frame_offset = 0;
  1331. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1332. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1333. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  1334. }
  1335. $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
  1336. $frame_offset += 3;
  1337. $parsedFrame['language'] = $frame_language;
  1338. $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
  1339. $parsedFrame['encodingid'] = $frame_textencoding;
  1340. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1341. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1342. if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
  1343. $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
  1344. }
  1345. unset($parsedFrame['data']);
  1346. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only)
  1347. // There may only be one 'OWNE' frame in a tag
  1348. // <Header for 'Ownership frame', ID: 'OWNE'>
  1349. // Text encoding $xx
  1350. // Price paid <text string> $00
  1351. // Date of purch. <text string>
  1352. // Seller <text string according to encoding>
  1353. $frame_offset = 0;
  1354. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1355. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1356. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  1357. }
  1358. $parsedFrame['encodingid'] = $frame_textencoding;
  1359. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1360. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1361. $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1362. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1363. $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
  1364. $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
  1365. $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
  1366. $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
  1367. if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
  1368. $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
  1369. }
  1370. $frame_offset += 8;
  1371. $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
  1372. unset($parsedFrame['data']);
  1373. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only)
  1374. // There may be more than one 'commercial frame' in a tag,
  1375. // but no two may be identical
  1376. // <Header for 'Commercial frame', ID: 'COMR'>
  1377. // Text encoding $xx
  1378. // Price string <text string> $00
  1379. // Valid until <text string>
  1380. // Contact URL <text string> $00
  1381. // Received as $xx
  1382. // Name of seller <text string according to encoding> $00 (00)
  1383. // Description <text string according to encoding> $00 (00)
  1384. // Picture MIME type <string> $00
  1385. // Seller logo <binary data>
  1386. $frame_offset = 0;
  1387. $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1388. if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
  1389. $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
  1390. }
  1391. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1392. $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1393. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1394. $frame_rawpricearray = explode('/', $frame_pricestring);
  1395. foreach ($frame_rawpricearray as $key => $val) {
  1396. $frame_currencyid = substr($val, 0, 3);
  1397. $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
  1398. $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
  1399. }
  1400. $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
  1401. $frame_offset += 8;
  1402. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1403. $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1404. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1405. $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1406. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  1407. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  1408. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1409. }
  1410. $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1411. if (ord($frame_sellername) === 0) {
  1412. $frame_sellername = '';
  1413. }
  1414. $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
  1415. $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
  1416. if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
  1417. $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
  1418. }
  1419. $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1420. if (ord($frame_description) === 0) {
  1421. $frame_description = '';
  1422. }
  1423. $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
  1424. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1425. $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1426. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1427. $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
  1428. $parsedFrame['encodingid'] = $frame_textencoding;
  1429. $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
  1430. $parsedFrame['pricevaliduntil'] = $frame_datestring;
  1431. $parsedFrame['contacturl'] = $frame_contacturl;
  1432. $parsedFrame['receivedasid'] = $frame_receivedasid;
  1433. $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
  1434. $parsedFrame['sellername'] = $frame_sellername;
  1435. $parsedFrame['description'] = $frame_description;
  1436. $parsedFrame['mime'] = $frame_mimetype;
  1437. $parsedFrame['logo'] = $frame_sellerlogo;
  1438. unset($parsedFrame['data']);
  1439. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only)
  1440. // There may be several 'ENCR' frames in a tag,
  1441. // but only one containing the same symbol
  1442. // and only one containing the same owner identifier
  1443. // <Header for 'Encryption method registration', ID: 'ENCR'>
  1444. // Owner identifier <text string> $00
  1445. // Method symbol $xx
  1446. // Encryption data <binary data>
  1447. $frame_offset = 0;
  1448. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1449. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1450. if (ord($frame_ownerid) === 0) {
  1451. $frame_ownerid = '';
  1452. }
  1453. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1454. $parsedFrame['ownerid'] = $frame_ownerid;
  1455. $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1456. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1457. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only)
  1458. // There may be several 'GRID' frames in a tag,
  1459. // but only one containing the same symbol
  1460. // and only one containing the same owner identifier
  1461. // <Header for 'Group ID registration', ID: 'GRID'>
  1462. // Owner identifier <text string> $00
  1463. // Group symbol $xx
  1464. // Group dependent data <binary data>
  1465. $frame_offset = 0;
  1466. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1467. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1468. if (ord($frame_ownerid) === 0) {
  1469. $frame_ownerid = '';
  1470. }
  1471. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1472. $parsedFrame['ownerid'] = $frame_ownerid;
  1473. $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1474. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1475. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only)
  1476. // The tag may contain more than one 'PRIV' frame
  1477. // but only with different contents
  1478. // <Header for 'Private frame', ID: 'PRIV'>
  1479. // Owner identifier <text string> $00
  1480. // The private data <binary data>
  1481. $frame_offset = 0;
  1482. $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
  1483. $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
  1484. if (ord($frame_ownerid) === 0) {
  1485. $frame_ownerid = '';
  1486. }
  1487. $frame_offset = $frame_terminatorpos + strlen("\x00");
  1488. $parsedFrame['ownerid'] = $frame_ownerid;
  1489. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1490. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only)
  1491. // There may be more than one 'signature frame' in a tag,
  1492. // but no two may be identical
  1493. // <Header for 'Signature frame', ID: 'SIGN'>
  1494. // Group symbol $xx
  1495. // Signature <binary data>
  1496. $frame_offset = 0;
  1497. $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1498. $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
  1499. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only)
  1500. // There may only be one 'seek frame' in a tag
  1501. // <Header for 'Seek frame', ID: 'SEEK'>
  1502. // Minimum offset to next tag $xx xx xx xx
  1503. $frame_offset = 0;
  1504. $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1505. } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only)
  1506. // There may only be one 'audio seek point index' frame in a tag
  1507. // <Header for 'Seek Point Index', ID: 'ASPI'>
  1508. // Indexed data start (S) $xx xx xx xx
  1509. // Indexed data length (L) $xx xx xx xx
  1510. // Number of index points (N) $xx xx
  1511. // Bits per index point (b) $xx
  1512. // Then for every index point the following data is included:
  1513. // Fraction at index (Fi) $xx (xx)
  1514. $frame_offset = 0;
  1515. $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1516. $frame_offset += 4;
  1517. $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
  1518. $frame_offset += 4;
  1519. $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
  1520. $frame_offset += 2;
  1521. $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
  1522. $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
  1523. for ($i = 0; $i < $frame_indexpoints; $i++) {
  1524. $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
  1525. $frame_offset += $frame_bytesperpoint;
  1526. }
  1527. unset($parsedFrame['data']);
  1528. } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment
  1529. // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
  1530. // There may only be one 'RGAD' frame in a tag
  1531. // <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
  1532. // Peak Amplitude $xx $xx $xx $xx
  1533. // Radio Replay Gain Adjustment %aaabbbcd %dddddddd
  1534. // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd
  1535. // a - name code
  1536. // b - originator code
  1537. // c - sign bit
  1538. // d - replay gain adjustment
  1539. $frame_offset = 0;
  1540. $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
  1541. $frame_offset += 4;
  1542. $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
  1543. $frame_offset += 2;
  1544. $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
  1545. $frame_offset += 2;
  1546. $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
  1547. $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
  1548. $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
  1549. $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
  1550. $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
  1551. $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
  1552. $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
  1553. $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
  1554. $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
  1555. $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
  1556. $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
  1557. $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
  1558. $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
  1559. $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
  1560. $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
  1561. $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
  1562. $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
  1563. $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
  1564. $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
  1565. unset($parsedFrame['data']);
  1566. }
  1567. return true;
  1568. }
  1569. function DeUnsynchronise($data) {
  1570. return str_replace("\xFF\x00", "\xFF", $data);
  1571. }
  1572. function LookupExtendedHeaderRestrictionsTagSizeLimits($index) {
  1573. static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
  1574. 0x00 => 'No more than 128 frames and 1 MB total tag size',
  1575. 0x01 => 'No more than 64 frames and 128 KB total tag size',
  1576. 0x02 => 'No more than 32 frames and 40 KB total tag size',
  1577. 0x03 => 'No more than 32 frames and 4 KB total tag size',
  1578. );
  1579. return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
  1580. }
  1581. function LookupExtendedHeaderRestrictionsTextEncodings($index) {
  1582. static $LookupExtendedHeaderRestrictionsTextEncodings = array(
  1583. 0x00 => 'No restrictions',
  1584. 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8',
  1585. );
  1586. return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
  1587. }
  1588. function LookupExtendedHeaderRestrictionsTextFieldSize($index) {
  1589. static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
  1590. 0x00 => 'No restrictions',
  1591. 0x01 => 'No string is longer than 1024 characters',
  1592. 0x02 => 'No string is longer than 128 characters',
  1593. 0x03 => 'No string is longer than 30 characters',
  1594. );
  1595. return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
  1596. }
  1597. function LookupExtendedHeaderRestrictionsImageEncoding($index) {
  1598. static $LookupExtendedHeaderRestrictionsImageEncoding = array(
  1599. 0x00 => 'No restrictions',
  1600. 0x01 => 'Images are encoded only with PNG or JPEG',
  1601. );
  1602. return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
  1603. }
  1604. function LookupExtendedHeaderRestrictionsImageSizeSize($index) {
  1605. static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
  1606. 0x00 => 'No restrictions',
  1607. 0x01 => 'All images are 256x256 pixels or smaller',
  1608. 0x02 => 'All images are 64x64 pixels or smaller',
  1609. 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise',
  1610. );
  1611. return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
  1612. }
  1613. function LookupCurrencyUnits($currencyid) {
  1614. $begin = __LINE__;
  1615. /** This is not a comment!
  1616. AED Dirhams
  1617. AFA Afghanis
  1618. ALL Leke
  1619. AMD Drams
  1620. ANG Guilders
  1621. AOA Kwanza
  1622. ARS Pesos
  1623. ATS Schillings
  1624. AUD Dollars
  1625. AWG Guilders
  1626. AZM Manats
  1627. BAM Convertible Marka
  1628. BBD Dollars
  1629. BDT Taka
  1630. BEF Francs
  1631. BGL Leva
  1632. BHD Dinars
  1633. BIF Francs
  1634. BMD Dollars
  1635. BND Dollars
  1636. BOB Bolivianos
  1637. BRL Brazil Real
  1638. BSD Dollars
  1639. BTN Ngultrum
  1640. BWP Pulas
  1641. BYR Rubles
  1642. BZD Dollars
  1643. CAD Dollars
  1644. CDF Congolese Francs
  1645. CHF Francs
  1646. CLP Pesos
  1647. CNY Yuan Renminbi
  1648. COP Pesos
  1649. CRC Colones
  1650. CUP Pesos
  1651. CVE Escudos
  1652. CYP Pounds
  1653. CZK Koruny
  1654. DEM Deutsche Marks
  1655. DJF Francs
  1656. DKK Kroner
  1657. DOP Pesos
  1658. DZD Algeria Dinars
  1659. EEK Krooni
  1660. EGP Pounds
  1661. ERN Nakfa
  1662. ESP Pesetas
  1663. ETB Birr
  1664. EUR Euro
  1665. FIM Markkaa
  1666. FJD Dollars
  1667. FKP Pounds
  1668. FRF Francs
  1669. GBP Pounds
  1670. GEL Lari
  1671. GGP Pounds
  1672. GHC Cedis
  1673. GIP Pounds
  1674. GMD Dalasi
  1675. GNF Francs
  1676. GRD Drachmae
  1677. GTQ Quetzales
  1678. GYD Dollars
  1679. HKD Dollars
  1680. HNL Lempiras
  1681. HRK Kuna
  1682. HTG Gourdes
  1683. HUF Forints
  1684. IDR Rupiahs
  1685. IEP Pounds
  1686. ILS New Shekels
  1687. IMP Pounds
  1688. INR Rupees
  1689. IQD Dinars
  1690. IRR Rials
  1691. ISK Kronur
  1692. ITL Lire
  1693. JEP Pounds
  1694. JMD Dollars
  1695. JOD Dinars
  1696. JPY Yen
  1697. KES Shillings
  1698. KGS Soms
  1699. KHR Riels
  1700. KMF Francs
  1701. KPW Won
  1702. KWD Dinars
  1703. KYD Dollars
  1704. KZT Tenge
  1705. LAK Kips
  1706. LBP Pounds
  1707. LKR Rupees
  1708. LRD Dollars
  1709. LSL Maloti
  1710. LTL Litai
  1711. LUF Francs
  1712. LVL Lati
  1713. LYD Dinars
  1714. MAD Dirhams
  1715. MDL Lei
  1716. MGF Malagasy Francs
  1717. MKD Denars
  1718. MMK Kyats
  1719. MNT Tugriks
  1720. MOP Patacas
  1721. MRO Ouguiyas
  1722. MTL Liri
  1723. MUR Rupees
  1724. MVR Rufiyaa
  1725. MWK Kwachas
  1726. MXN Pesos
  1727. MYR Ringgits
  1728. MZM Meticais
  1729. NAD Dollars
  1730. NGN Nairas
  1731. NIO Gold Cordobas
  1732. NLG Guilders
  1733. NOK Krone
  1734. NPR Nepal Rupees
  1735. NZD Dollars
  1736. OMR Rials
  1737. PAB Balboa
  1738. PEN Nuevos Soles
  1739. PGK Kina
  1740. PHP Pesos
  1741. PKR Rupees
  1742. PLN Zlotych
  1743. PTE Escudos
  1744. PYG Guarani
  1745. QAR Rials
  1746. ROL Lei
  1747. RUR Rubles
  1748. RWF Rwanda Francs
  1749. SAR Riyals
  1750. SBD Dollars
  1751. SCR Rupees
  1752. SDD Dinars
  1753. SEK Kronor
  1754. SGD Dollars
  1755. SHP Pounds
  1756. SIT Tolars
  1757. SKK Koruny
  1758. SLL Leones
  1759. SOS Shillings
  1760. SPL Luigini
  1761. SRG Guilders
  1762. STD Dobras
  1763. SVC Colones
  1764. SYP Pounds
  1765. SZL Emalangeni
  1766. THB Baht
  1767. TJR Rubles
  1768. TMM Manats
  1769. TND Dinars
  1770. TOP Pa'anga
  1771. TRL Liras
  1772. TTD Dollars
  1773. TVD Tuvalu Dollars
  1774. TWD New Dollars
  1775. TZS Shillings
  1776. UAH Hryvnia
  1777. UGX Shillings
  1778. USD Dollars
  1779. UYU Pesos
  1780. UZS Sums
  1781. VAL Lire
  1782. VEB Bolivares
  1783. VND Dong
  1784. VUV Vatu
  1785. WST Tala
  1786. XAF Francs
  1787. XAG Ounces
  1788. XAU Ounces
  1789. XCD Dollars
  1790. XDR Special Drawing Rights
  1791. XPD Ounces
  1792. XPF Francs
  1793. XPT Ounces
  1794. YER Rials
  1795. YUM New Dinars
  1796. ZAR Rand
  1797. ZMK Kwacha
  1798. ZWD Zimbabwe Dollars
  1799. */
  1800. return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
  1801. }
  1802. function LookupCurrencyCountry($currencyid) {
  1803. $begin = __LINE__;
  1804. /** This is not a comment!
  1805. AED United Arab Emirates
  1806. AFA Afghanistan
  1807. ALL Albania
  1808. AMD Armenia
  1809. ANG Netherlands Antilles
  1810. AOA Angola
  1811. ARS Argentina
  1812. ATS Austria
  1813. AUD Australia
  1814. AWG Aruba
  1815. AZM Azerbaijan
  1816. BAM Bosnia and Herzegovina
  1817. BBD Barbados
  1818. BDT Bangladesh
  1819. BEF Belgium
  1820. BGL Bulgaria
  1821. BHD Bahrain
  1822. BIF Burundi
  1823. BMD Bermuda
  1824. BND Brunei Darussalam
  1825. BOB Bolivia
  1826. BRL Brazil
  1827. BSD Bahamas
  1828. BTN Bhutan
  1829. BWP Botswana
  1830. BYR Belarus
  1831. BZD Belize
  1832. CAD Canada
  1833. CDF Congo/Kinshasa
  1834. CHF Switzerland
  1835. CLP Chile
  1836. CNY China
  1837. COP Colombia
  1838. CRC Costa Rica
  1839. CUP Cuba
  1840. CVE Cape Verde
  1841. CYP Cyprus
  1842. CZK Czech Republic
  1843. DEM Germany
  1844. DJF Djibouti
  1845. DKK Denmark
  1846. DOP Dominican Republic
  1847. DZD Algeria
  1848. EEK Estonia
  1849. EGP Egypt
  1850. ERN Eritrea
  1851. ESP Spain
  1852. ETB Ethiopia
  1853. EUR Euro Member Countries
  1854. FIM Finland
  1855. FJD Fiji
  1856. FKP Falkland Islands (Malvinas)
  1857. FRF France
  1858. GBP United Kingdom
  1859. GEL Georgia
  1860. GGP Guernsey
  1861. GHC Ghana
  1862. GIP Gibraltar
  1863. GMD Gambia
  1864. GNF Guinea
  1865. GRD Greece
  1866. GTQ Guatemala
  1867. GYD Guyana
  1868. HKD Hong Kong
  1869. HNL Honduras
  1870. HRK Croatia
  1871. HTG Haiti
  1872. HUF Hungary
  1873. IDR Indonesia
  1874. IEP Ireland (Eire)
  1875. ILS Israel
  1876. IMP Isle of Man
  1877. INR India
  1878. IQD Iraq
  1879. IRR Iran
  1880. ISK Iceland
  1881. ITL Italy
  1882. JEP Jersey
  1883. JMD Jamaica
  1884. JOD Jordan
  1885. JPY Japan
  1886. KES Kenya
  1887. KGS Kyrgyzstan
  1888. KHR Cambodia
  1889. KMF Comoros
  1890. KPW Korea
  1891. KWD Kuwait
  1892. KYD Cayman Islands
  1893. KZT Kazakstan
  1894. LAK Laos
  1895. LBP Lebanon
  1896. LKR Sri Lanka
  1897. LRD Liberia
  1898. LSL Lesotho
  1899. LTL Lithuania
  1900. LUF Luxembourg
  1901. LVL Latvia
  1902. LYD Libya
  1903. MAD Morocco
  1904. MDL Moldova
  1905. MGF Madagascar
  1906. MKD Macedonia
  1907. MMK Myanmar (Burma)
  1908. MNT Mongolia
  1909. MOP Macau
  1910. MRO Mauritania
  1911. MTL Malta
  1912. MUR Mauritius
  1913. MVR Maldives (Maldive Islands)
  1914. MWK Malawi
  1915. MXN Mexico
  1916. MYR Malaysia
  1917. MZM Mozambique
  1918. NAD Namibia
  1919. NGN Nigeria
  1920. NIO Nicaragua
  1921. NLG Netherlands (Holland)
  1922. NOK Norway
  1923. NPR Nepal
  1924. NZD New Zealand
  1925. OMR Oman
  1926. PAB Panama
  1927. PEN Peru
  1928. PGK Papua New Guinea
  1929. PHP Philippines
  1930. PKR Pakistan
  1931. PLN Poland
  1932. PTE Portugal
  1933. PYG Paraguay
  1934. QAR Qatar
  1935. ROL Romania
  1936. RUR Russia
  1937. RWF Rwanda
  1938. SAR Saudi Arabia
  1939. SBD Solomon Islands
  1940. SCR Seychelles
  1941. SDD Sudan
  1942. SEK Sweden
  1943. SGD Singapore
  1944. SHP Saint Helena
  1945. SIT Slovenia
  1946. SKK Slovakia
  1947. SLL Sierra Leone
  1948. SOS Somalia
  1949. SPL Seborga
  1950. SRG Suriname
  1951. STD S�o Tome and Principe
  1952. SVC El Salvador
  1953. SYP Syria
  1954. SZL Swaziland
  1955. THB Thailand
  1956. TJR Tajikistan
  1957. TMM Turkmenistan
  1958. TND Tunisia
  1959. TOP Tonga
  1960. TRL Turkey
  1961. TTD Trinidad and Tobago
  1962. TVD Tuvalu
  1963. TWD Taiwan
  1964. TZS Tanzania
  1965. UAH Ukraine
  1966. UGX Uganda
  1967. USD United States of America
  1968. UYU Uruguay
  1969. UZS Uzbekistan
  1970. VAL Vatican City
  1971. VEB Venezuela
  1972. VND Viet Nam
  1973. VUV Vanuatu
  1974. WST Samoa
  1975. XAF Communaut� Financi�re Africaine
  1976. XAG Silver
  1977. XAU Gold
  1978. XCD East Caribbean
  1979. XDR International Monetary Fund
  1980. XPD Palladium
  1981. XPF Comptoirs Fran�ais du Pacifique
  1982. XPT Platinum
  1983. YER Yemen
  1984. YUM Yugoslavia
  1985. ZAR South Africa
  1986. ZMK Zambia
  1987. ZWD Zimbabwe
  1988. */
  1989. return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
  1990. }
  1991. static function LanguageLookup($languagecode, $casesensitive=false) {
  1992. if (!$casesensitive) {
  1993. $languagecode = strtolower($languagecode);
  1994. }
  1995. // http://www.id3.org/id3v2.4.0-structure.txt
  1996. // [4. ID3v2 frame overview]
  1997. // The three byte language field, present in several frames, is used to
  1998. // describe the language of the frame's content, according to ISO-639-2
  1999. // [ISO-639-2]. The language should be represented in lower case. If the
  2000. // language is not known the string "XXX" should be used.
  2001. // ISO 639-2 - http://www.id3.org/iso639-2.html
  2002. $begin = __LINE__;
  2003. /** This is not a comment!
  2004. XXX unknown
  2005. xxx unknown
  2006. aar Afar
  2007. abk Abkhazian
  2008. ace Achinese
  2009. ach Acoli
  2010. ada Adangme
  2011. afa Afro-Asiatic (Other)
  2012. afh Afrihili
  2013. afr Afrikaans
  2014. aka Akan
  2015. akk Akkadian
  2016. alb Albanian
  2017. ale Aleut
  2018. alg Algonquian Languages
  2019. amh Amharic
  2020. ang English, Old (ca. 450-1100)
  2021. apa Apache Languages
  2022. ara Arabic
  2023. arc Aramaic
  2024. arm Armenian
  2025. arn Araucanian
  2026. arp Arapaho
  2027. art Artificial (Other)
  2028. arw Arawak
  2029. asm Assamese
  2030. ath Athapascan Languages
  2031. ava Avaric
  2032. ave Avestan
  2033. awa Awadhi
  2034. aym Aymara
  2035. aze Azerbaijani
  2036. bad Banda
  2037. bai Bamileke Languages
  2038. bak Bashkir
  2039. bal Baluchi
  2040. bam Bambara
  2041. ban Balinese
  2042. baq Basque
  2043. bas Basa
  2044. bat Baltic (Other)
  2045. bej Beja
  2046. bel Byelorussian
  2047. bem Bemba
  2048. ben Bengali
  2049. ber Berber (Other)
  2050. bho Bhojpuri
  2051. bih Bihari
  2052. bik Bikol
  2053. bin Bini
  2054. bis Bislama
  2055. bla Siksika
  2056. bnt Bantu (Other)
  2057. bod Tibetan
  2058. bra Braj
  2059. bre Breton
  2060. bua Buriat
  2061. bug Buginese
  2062. bul Bulgarian
  2063. bur Burmese
  2064. cad Caddo
  2065. cai Central American Indian (Other)
  2066. car Carib
  2067. cat Catalan
  2068. cau Caucasian (Other)
  2069. ceb Cebuano
  2070. cel Celtic (Other)
  2071. ces Czech
  2072. cha Chamorro
  2073. chb Chibcha
  2074. che Chechen
  2075. chg Chagatai
  2076. chi Chinese
  2077. chm Mari
  2078. chn Chinook jargon
  2079. cho Choctaw
  2080. chr Cherokee
  2081. chu Church Slavic
  2082. chv Chuvash
  2083. chy Cheyenne
  2084. cop Coptic
  2085. cor Cornish
  2086. cos Corsican
  2087. cpe Creoles and Pidgins, English-based (Other)
  2088. cpf Creoles and Pidgins, French-based (Other)
  2089. cpp Creoles and Pidgins, Portuguese-based (Other)
  2090. cre Cree
  2091. crp Creoles and Pidgins (Other)
  2092. cus Cushitic (Other)
  2093. cym Welsh
  2094. cze Czech
  2095. dak Dakota
  2096. dan Danish
  2097. del Delaware
  2098. deu German
  2099. din Dinka
  2100. div Divehi
  2101. doi Dogri
  2102. dra Dravidian (Other)
  2103. dua Duala
  2104. dum Dutch, Middle (ca. 1050-1350)
  2105. dut Dutch
  2106. dyu Dyula
  2107. dzo Dzongkha
  2108. efi Efik
  2109. egy Egyptian (Ancient)
  2110. eka Ekajuk
  2111. ell Greek, Modern (1453-)
  2112. elx Elamite
  2113. eng English
  2114. enm English, Middle (ca. 1100-1500)
  2115. epo Esperanto
  2116. esk Eskimo (Other)
  2117. esl Spanish
  2118. est Estonian
  2119. eus Basque
  2120. ewe Ewe
  2121. ewo Ewondo
  2122. fan Fang
  2123. fao Faroese
  2124. fas Persian
  2125. fat Fanti
  2126. fij Fijian
  2127. fin Finnish
  2128. fiu Finno-Ugrian (Other)
  2129. fon Fon
  2130. fra French
  2131. fre French
  2132. frm French, Middle (ca. 1400-1600)
  2133. fro French, Old (842- ca. 1400)
  2134. fry Frisian
  2135. ful Fulah
  2136. gaa Ga
  2137. gae Gaelic (Scots)
  2138. gai Irish
  2139. gay Gayo
  2140. gdh Gaelic (Scots)
  2141. gem Germanic (Other)
  2142. geo Georgian
  2143. ger German
  2144. gez Geez
  2145. gil Gilbertese
  2146. glg Gallegan
  2147. gmh German, Middle High (ca. 1050-1500)
  2148. goh German, Old High (ca. 750-1050)
  2149. gon Gondi
  2150. got Gothic
  2151. grb Grebo
  2152. grc Greek, Ancient (to 1453)
  2153. gre Greek, Modern (1453-)
  2154. grn Guarani
  2155. guj Gujarati
  2156. hai Haida
  2157. hau Hausa
  2158. haw Hawaiian
  2159. heb Hebrew
  2160. her Herero
  2161. hil Hiligaynon
  2162. him Himachali
  2163. hin Hindi
  2164. hmo Hiri Motu
  2165. hun Hungarian
  2166. hup Hupa
  2167. hye Armenian
  2168. iba Iban
  2169. ibo Igbo
  2170. ice Icelandic
  2171. ijo Ijo
  2172. iku Inuktitut
  2173. ilo Iloko
  2174. ina Interlingua (International Auxiliary language Association)
  2175. inc Indic (Other)
  2176. ind Indonesian
  2177. ine Indo-European (Other)
  2178. ine Interlingue
  2179. ipk Inupiak
  2180. ira Iranian (Other)
  2181. iri Irish
  2182. iro Iroquoian uages
  2183. isl Icelandic
  2184. ita Italian
  2185. jav Javanese
  2186. jaw Javanese
  2187. jpn Japanese
  2188. jpr Judeo-Persian
  2189. jrb Judeo-Arabic
  2190. kaa Kara-Kalpak
  2191. kab Kabyle
  2192. kac Kachin
  2193. kal Greenlandic
  2194. kam Kamba
  2195. kan Kannada
  2196. kar Karen
  2197. kas Kashmiri
  2198. kat Georgian
  2199. kau Kanuri
  2200. kaw Kawi
  2201. kaz Kazakh
  2202. kha Khasi
  2203. khi Khoisan (Other)
  2204. khm Khmer
  2205. kho Khotanese
  2206. kik Kikuyu
  2207. kin Kinyarwanda
  2208. kir Kirghiz
  2209. kok Konkani
  2210. kom Komi
  2211. kon Kongo
  2212. kor Korean
  2213. kpe Kpelle
  2214. kro Kru
  2215. kru Kurukh
  2216. kua Kuanyama
  2217. kum Kumyk
  2218. kur Kurdish
  2219. kus Kusaie
  2220. kut Kutenai
  2221. lad Ladino
  2222. lah Lahnda
  2223. lam Lamba
  2224. lao Lao
  2225. lat Latin
  2226. lav Latvian
  2227. lez Lezghian
  2228. lin Lingala
  2229. lit Lithuanian
  2230. lol Mongo
  2231. loz Lozi
  2232. ltz Letzeburgesch
  2233. lub Luba-Katanga
  2234. lug Ganda
  2235. lui Luiseno
  2236. lun Lunda
  2237. luo Luo (Kenya and Tanzania)
  2238. mac Macedonian
  2239. mad Madurese
  2240. mag Magahi
  2241. mah Marshall
  2242. mai Maithili
  2243. mak Macedonian
  2244. mak Makasar
  2245. mal Malayalam
  2246. man Mandingo
  2247. mao Maori
  2248. map Austronesian (Other)
  2249. mar Marathi
  2250. mas Masai
  2251. max Manx
  2252. may Malay
  2253. men Mende
  2254. mga Irish, Middle (900 - 1200)
  2255. mic Micmac
  2256. min Minangkabau
  2257. mis Miscellaneous (Other)
  2258. mkh Mon-Kmer (Other)
  2259. mlg Malagasy
  2260. mlt Maltese
  2261. mni Manipuri
  2262. mno Manobo Languages
  2263. moh Mohawk
  2264. mol Moldavian
  2265. mon Mongolian
  2266. mos Mossi
  2267. mri Maori
  2268. msa Malay
  2269. mul Multiple Languages
  2270. mun Munda Languages
  2271. mus Creek
  2272. mwr Marwari
  2273. mya Burmese
  2274. myn Mayan Languages
  2275. nah Aztec
  2276. nai North American Indian (Other)
  2277. nau Nauru
  2278. nav Navajo
  2279. nbl Ndebele, South
  2280. nde Ndebele, North
  2281. ndo Ndongo
  2282. nep Nepali
  2283. new Newari
  2284. nic Niger-Kordofanian (Other)
  2285. niu Niuean
  2286. nla Dutch
  2287. nno Norwegian (Nynorsk)
  2288. non Norse, Old
  2289. nor Norwegian
  2290. nso Sotho, Northern
  2291. nub Nubian Languages
  2292. nya Nyanja
  2293. nym Nyamwezi
  2294. nyn Nyankole
  2295. nyo Nyoro
  2296. nzi Nzima
  2297. oci Langue d'Oc (post 1500)
  2298. oji Ojibwa
  2299. ori Oriya
  2300. orm Oromo
  2301. osa Osage
  2302. oss Ossetic
  2303. ota Turkish, Ottoman (1500 - 1928)
  2304. oto Otomian Languages
  2305. paa Papuan-Australian (Other)
  2306. pag Pangasinan
  2307. pal Pahlavi
  2308. pam Pampanga
  2309. pan Panjabi
  2310. pap Papiamento
  2311. pau Palauan
  2312. peo Persian, Old (ca 600 - 400 B.C.)
  2313. per Persian
  2314. phn Phoenician
  2315. pli Pali
  2316. pol Polish
  2317. pon Ponape
  2318. por Portuguese
  2319. pra Prakrit uages
  2320. pro Provencal, Old (to 1500)
  2321. pus Pushto
  2322. que Quechua
  2323. raj Rajasthani
  2324. rar Rarotongan
  2325. roa Romance (Other)
  2326. roh Rhaeto-Romance
  2327. rom Romany
  2328. ron Romanian
  2329. rum Romanian
  2330. run Rundi
  2331. rus Russian
  2332. sad Sandawe
  2333. sag Sango
  2334. sah Yakut
  2335. sai South American Indian (Other)
  2336. sal Salishan Languages
  2337. sam Samaritan Aramaic
  2338. san Sanskrit
  2339. sco Scots
  2340. scr Serbo-Croatian
  2341. sel Selkup
  2342. sem Semitic (Other)
  2343. sga Irish, Old (to 900)
  2344. shn Shan
  2345. sid Sidamo
  2346. sin Singhalese
  2347. sio Siouan Languages
  2348. sit Sino-Tibetan (Other)
  2349. sla Slavic (Other)
  2350. slk Slovak
  2351. slo Slovak
  2352. slv Slovenian
  2353. smi Sami Languages
  2354. smo Samoan
  2355. sna Shona
  2356. snd Sindhi
  2357. sog Sogdian
  2358. som Somali
  2359. son Songhai
  2360. sot Sotho, Southern
  2361. spa Spanish
  2362. sqi Albanian
  2363. srd Sardinian
  2364. srr Serer
  2365. ssa Nilo-Saharan (Other)
  2366. ssw Siswant
  2367. ssw Swazi
  2368. suk Sukuma
  2369. sun Sudanese
  2370. sus Susu
  2371. sux Sumerian
  2372. sve Swedish
  2373. swa Swahili
  2374. swe Swedish
  2375. syr Syriac
  2376. tah Tahitian
  2377. tam Tamil
  2378. tat Tatar
  2379. tel Telugu
  2380. tem Timne
  2381. ter Tereno
  2382. tgk Tajik
  2383. tgl Tagalog
  2384. tha Thai
  2385. tib Tibetan
  2386. tig Tigre
  2387. tir Tigrinya
  2388. tiv Tivi
  2389. tli Tlingit
  2390. tmh Tamashek
  2391. tog Tonga (Nyasa)
  2392. ton Tonga (Tonga Islands)
  2393. tru Truk
  2394. tsi Tsimshian
  2395. tsn Tswana
  2396. tso Tsonga
  2397. tuk Turkmen
  2398. tum Tumbuka
  2399. tur Turkish
  2400. tut Altaic (Other)
  2401. twi Twi
  2402. tyv Tuvinian
  2403. uga Ugaritic
  2404. uig Uighur
  2405. ukr Ukrainian
  2406. umb Umbundu
  2407. und Undetermined
  2408. urd Urdu
  2409. uzb Uzbek
  2410. vai Vai
  2411. ven Venda
  2412. vie Vietnamese
  2413. vol Volap�k
  2414. vot Votic
  2415. wak Wakashan Languages
  2416. wal Walamo
  2417. war Waray
  2418. was Washo
  2419. wel Welsh
  2420. wen Sorbian Languages
  2421. wol Wolof
  2422. xho Xhosa
  2423. yao Yao
  2424. yap Yap
  2425. yid Yiddish
  2426. yor Yoruba
  2427. zap Zapotec
  2428. zen Zenaga
  2429. zha Zhuang
  2430. zho Chinese
  2431. zul Zulu
  2432. zun Zuni
  2433. */
  2434. return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
  2435. }
  2436. static function ETCOEventLookup($index) {
  2437. if (($index >= 0x17) && ($index <= 0xDF)) {
  2438. return 'reserved for future use';
  2439. }
  2440. if (($index >= 0xE0) && ($index <= 0xEF)) {
  2441. return 'not predefined synch 0-F';
  2442. }
  2443. if (($index >= 0xF0) && ($index <= 0xFC)) {
  2444. return 'reserved for future use';
  2445. }
  2446. static $EventLookup = array(
  2447. 0x00 => 'padding (has no meaning)',
  2448. 0x01 => 'end of initial silence',
  2449. 0x02 => 'intro start',
  2450. 0x03 => 'main part start',
  2451. 0x04 => 'outro start',
  2452. 0x05 => 'outro end',
  2453. 0x06 => 'verse start',
  2454. 0x07 => 'refrain start',
  2455. 0x08 => 'interlude start',
  2456. 0x09 => 'theme start',
  2457. 0x0A => 'variation start',
  2458. 0x0B => 'key change',
  2459. 0x0C => 'time change',
  2460. 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)',
  2461. 0x0E => 'sustained noise',
  2462. 0x0F => 'sustained noise end',
  2463. 0x10 => 'intro end',
  2464. 0x11 => 'main part end',
  2465. 0x12 => 'verse end',
  2466. 0x13 => 'refrain end',
  2467. 0x14 => 'theme end',
  2468. 0x15 => 'profanity',
  2469. 0x16 => 'profanity end',
  2470. 0xFD => 'audio end (start of silence)',
  2471. 0xFE => 'audio file ends',
  2472. 0xFF => 'one more byte of events follows'
  2473. );
  2474. return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
  2475. }
  2476. static function SYTLContentTypeLookup($index) {
  2477. static $SYTLContentTypeLookup = array(
  2478. 0x00 => 'other',
  2479. 0x01 => 'lyrics',
  2480. 0x02 => 'text transcription',
  2481. 0x03 => 'movement/part name', // (e.g. 'Adagio')
  2482. 0x04 => 'events', // (e.g. 'Don Quijote enters the stage')
  2483. 0x05 => 'chord', // (e.g. 'Bb F Fsus')
  2484. 0x06 => 'trivia/\'pop up\' information',
  2485. 0x07 => 'URLs to webpages',
  2486. 0x08 => 'URLs to images'
  2487. );
  2488. return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
  2489. }
  2490. static function APICPictureTypeLookup($index, $returnarray=false) {
  2491. static $APICPictureTypeLookup = array(
  2492. 0x00 => 'Other',
  2493. 0x01 => '32x32 pixels \'file icon\' (PNG only)',
  2494. 0x02 => 'Other file icon',
  2495. 0x03 => 'Cover (front)',
  2496. 0x04 => 'Cover (back)',
  2497. 0x05 => 'Leaflet page',
  2498. 0x06 => 'Media (e.g. label side of CD)',
  2499. 0x07 => 'Lead artist/lead performer/soloist',
  2500. 0x08 => 'Artist/performer',
  2501. 0x09 => 'Conductor',
  2502. 0x0A => 'Band/Orchestra',
  2503. 0x0B => 'Composer',
  2504. 0x0C => 'Lyricist/text writer',
  2505. 0x0D => 'Recording Location',
  2506. 0x0E => 'During recording',
  2507. 0x0F => 'During performance',
  2508. 0x10 => 'Movie/video screen capture',
  2509. 0x11 => 'A bright coloured fish',
  2510. 0x12 => 'Illustration',
  2511. 0x13 => 'Band/artist logotype',
  2512. 0x14 => 'Publisher/Studio logotype'
  2513. );
  2514. if ($returnarray) {
  2515. return $APICPictureTypeLookup;
  2516. }
  2517. return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
  2518. }
  2519. static function COMRReceivedAsLookup($index) {
  2520. static $COMRReceivedAsLookup = array(
  2521. 0x00 => 'Other',
  2522. 0x01 => 'Standard CD album with other songs',
  2523. 0x02 => 'Compressed audio on CD',
  2524. 0x03 => 'File over the Internet',
  2525. 0x04 => 'Stream over the Internet',
  2526. 0x05 => 'As note sheets',
  2527. 0x06 => 'As note sheets in a book with other sheets',
  2528. 0x07 => 'Music on other media',
  2529. 0x08 => 'Non-musical merchandise'
  2530. );
  2531. return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
  2532. }
  2533. static function RVA2ChannelTypeLookup($index) {
  2534. static $RVA2ChannelTypeLookup = array(
  2535. 0x00 => 'Other',
  2536. 0x01 => 'Master volume',
  2537. 0x02 => 'Front right',
  2538. 0x03 => 'Front left',
  2539. 0x04 => 'Back right',
  2540. 0x05 => 'Back left',
  2541. 0x06 => 'Front centre',
  2542. 0x07 => 'Back centre',
  2543. 0x08 => 'Subwoofer'
  2544. );
  2545. return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
  2546. }
  2547. static function FrameNameLongLookup($framename) {
  2548. $begin = __LINE__;
  2549. /** This is not a comment!
  2550. AENC Audio encryption
  2551. APIC Attached picture
  2552. ASPI Audio seek point index
  2553. BUF Recommended buffer size
  2554. CNT Play counter
  2555. COM Comments
  2556. COMM Comments
  2557. COMR Commercial frame
  2558. CRA Audio encryption
  2559. CRM Encrypted meta frame
  2560. ENCR Encryption method registration
  2561. EQU Equalisation
  2562. EQU2 Equalisation (2)
  2563. EQUA Equalisation
  2564. ETC Event timing codes
  2565. ETCO Event timing codes
  2566. GEO General encapsulated object
  2567. GEOB General encapsulated object
  2568. GRID Group identification registration
  2569. IPL Involved people list
  2570. IPLS Involved people list
  2571. LINK Linked information
  2572. LNK Linked information
  2573. MCDI Music CD identifier
  2574. MCI Music CD Identifier
  2575. MLL MPEG location lookup table
  2576. MLLT MPEG location lookup table
  2577. OWNE Ownership frame
  2578. PCNT Play counter
  2579. PIC Attached picture
  2580. POP Popularimeter
  2581. POPM Popularimeter
  2582. POSS Position synchronisation frame
  2583. PRIV Private frame
  2584. RBUF Recommended buffer size
  2585. REV Reverb
  2586. RVA Relative volume adjustment
  2587. RVA2 Relative volume adjustment (2)
  2588. RVAD Relative volume adjustment
  2589. RVRB Reverb
  2590. SEEK Seek frame
  2591. SIGN Signature frame
  2592. SLT Synchronised lyric/text
  2593. STC Synced tempo codes
  2594. SYLT Synchronised lyric/text
  2595. SYTC Synchronised tempo codes
  2596. TAL Album/Movie/Show title
  2597. TALB Album/Movie/Show title
  2598. TBP BPM (Beats Per Minute)
  2599. TBPM BPM (beats per minute)
  2600. TCM Composer
  2601. TCMP Part of a compilation
  2602. TCO Content type
  2603. TCOM Composer
  2604. TCON Content type
  2605. TCOP Copyright message
  2606. TCP Part of a compilation
  2607. TCR Copyright message
  2608. TDA Date
  2609. TDAT Date
  2610. TDEN Encoding time
  2611. TDLY Playlist delay
  2612. TDOR Original release time
  2613. TDRC Recording time
  2614. TDRL Release time
  2615. TDTG Tagging time
  2616. TDY Playlist delay
  2617. TEN Encoded by
  2618. TENC Encoded by
  2619. TEXT Lyricist/Text writer
  2620. TFLT File type
  2621. TFT File type
  2622. TIM Time
  2623. TIME Time
  2624. TIPL Involved people list
  2625. TIT1 Content group description
  2626. TIT2 Title/songname/content description
  2627. TIT3 Subtitle/Description refinement
  2628. TKE Initial key
  2629. TKEY Initial key
  2630. TLA Language(s)
  2631. TLAN Language(s)
  2632. TLE Length
  2633. TLEN Length
  2634. TMCL Musician credits list
  2635. TMED Media type
  2636. TMOO Mood
  2637. TMT Media type
  2638. TOA Original artist(s)/performer(s)
  2639. TOAL Original album/movie/show title
  2640. TOF Original filename
  2641. TOFN Original filename
  2642. TOL Original Lyricist(s)/text writer(s)
  2643. TOLY Original lyricist(s)/text writer(s)
  2644. TOPE Original artist(s)/performer(s)
  2645. TOR Original release year
  2646. TORY Original release year
  2647. TOT Original album/Movie/Show title
  2648. TOWN File owner/licensee
  2649. TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
  2650. TP2 Band/Orchestra/Accompaniment
  2651. TP3 Conductor/Performer refinement
  2652. TP4 Interpreted, remixed, or otherwise modified by
  2653. TPA Part of a set
  2654. TPB Publisher
  2655. TPE1 Lead performer(s)/Soloist(s)
  2656. TPE2 Band/orchestra/accompaniment
  2657. TPE3 Conductor/performer refinement
  2658. TPE4 Interpreted, remixed, or otherwise modified by
  2659. TPOS Part of a set
  2660. TPRO Produced notice
  2661. TPUB Publisher
  2662. TRC ISRC (International Standard Recording Code)
  2663. TRCK Track number/Position in set
  2664. TRD Recording dates
  2665. TRDA Recording dates
  2666. TRK Track number/Position in set
  2667. TRSN Internet radio station name
  2668. TRSO Internet radio station owner
  2669. TS2 Album-Artist sort order
  2670. TSA Album sort order
  2671. TSC Composer sort order
  2672. TSI Size
  2673. TSIZ Size
  2674. TSO2 Album-Artist sort order
  2675. TSOA Album sort order
  2676. TSOC Composer sort order
  2677. TSOP Performer sort order
  2678. TSOT Title sort order
  2679. TSP Performer sort order
  2680. TSRC ISRC (international standard recording code)
  2681. TSS Software/hardware and settings used for encoding
  2682. TSSE Software/Hardware and settings used for encoding
  2683. TSST Set subtitle
  2684. TST Title sort order
  2685. TT1 Content group description
  2686. TT2 Title/Songname/Content description
  2687. TT3 Subtitle/Description refinement
  2688. TXT Lyricist/text writer
  2689. TXX User defined text information frame
  2690. TXXX User defined text information frame
  2691. TYE Year
  2692. TYER Year
  2693. UFI Unique file identifier
  2694. UFID Unique file identifier
  2695. ULT Unsychronised lyric/text transcription
  2696. USER Terms of use
  2697. USLT Unsynchronised lyric/text transcription
  2698. WAF Official audio file webpage
  2699. WAR Official artist/performer webpage
  2700. WAS Official audio source webpage
  2701. WCM Commercial information
  2702. WCOM Commercial information
  2703. WCOP Copyright/Legal information
  2704. WCP Copyright/Legal information
  2705. WOAF Official audio file webpage
  2706. WOAR Official artist/performer webpage
  2707. WOAS Official audio source webpage
  2708. WORS Official Internet radio station homepage
  2709. WPAY Payment
  2710. WPB Publishers official webpage
  2711. WPUB Publishers official webpage
  2712. WXX User defined URL link frame
  2713. WXXX User defined URL link frame
  2714. TFEA Featured Artist
  2715. TSTU Recording Studio
  2716. rgad Replay Gain Adjustment
  2717. */
  2718. return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long');
  2719. // Last three:
  2720. // from Helium2 [www.helium2.com]
  2721. // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
  2722. }
  2723. static function FrameNameShortLookup($framename) {
  2724. $begin = __LINE__;
  2725. /** This is not a comment!
  2726. AENC audio_encryption
  2727. APIC attached_picture
  2728. ASPI audio_seek_point_index
  2729. BUF recommended_buffer_size
  2730. CNT play_counter
  2731. COM comment
  2732. COMM comment
  2733. COMR commercial_frame
  2734. CRA audio_encryption
  2735. CRM encrypted_meta_frame
  2736. ENCR encryption_method_registration
  2737. EQU equalisation
  2738. EQU2 equalisation
  2739. EQUA equalisation
  2740. ETC event_timing_codes
  2741. ETCO event_timing_codes
  2742. GEO general_encapsulated_object
  2743. GEOB general_encapsulated_object
  2744. GRID group_identification_registration
  2745. IPL involved_people_list
  2746. IPLS involved_people_list
  2747. LINK linked_information
  2748. LNK linked_information
  2749. MCDI music_cd_identifier
  2750. MCI music_cd_identifier
  2751. MLL mpeg_location_lookup_table
  2752. MLLT mpeg_location_lookup_table
  2753. OWNE ownership_frame
  2754. PCNT play_counter
  2755. PIC attached_picture
  2756. POP popularimeter
  2757. POPM popularimeter
  2758. POSS position_synchronisation_frame
  2759. PRIV private_frame
  2760. RBUF recommended_buffer_size
  2761. REV reverb
  2762. RVA relative_volume_adjustment
  2763. RVA2 relative_volume_adjustment
  2764. RVAD relative_volume_adjustment
  2765. RVRB reverb
  2766. SEEK seek_frame
  2767. SIGN signature_frame
  2768. SLT synchronised_lyric
  2769. STC synced_tempo_codes
  2770. SYLT synchronised_lyric
  2771. SYTC synchronised_tempo_codes
  2772. TAL album
  2773. TALB album
  2774. TBP bpm
  2775. TBPM bpm
  2776. TCM composer
  2777. TCMP part_of_a_compilation
  2778. TCO genre
  2779. TCOM composer
  2780. TCON genre
  2781. TCOP copyright_message
  2782. TCP part_of_a_compilation
  2783. TCR copyright_message
  2784. TDA date
  2785. TDAT date
  2786. TDEN encoding_time
  2787. TDLY playlist_delay
  2788. TDOR original_release_time
  2789. TDRC recording_time
  2790. TDRL release_time
  2791. TDTG tagging_time
  2792. TDY playlist_delay
  2793. TEN encoded_by
  2794. TENC encoded_by
  2795. TEXT lyricist
  2796. TFLT file_type
  2797. TFT file_type
  2798. TIM time
  2799. TIME time
  2800. TIPL involved_people_list
  2801. TIT1 content_group_description
  2802. TIT2 title
  2803. TIT3 subtitle
  2804. TKE initial_key
  2805. TKEY initial_key
  2806. TLA language
  2807. TLAN language
  2808. TLE length
  2809. TLEN length
  2810. TMCL musician_credits_list
  2811. TMED media_type
  2812. TMOO mood
  2813. TMT media_type
  2814. TOA original_artist
  2815. TOAL original_album
  2816. TOF original_filename
  2817. TOFN original_filename
  2818. TOL original_lyricist
  2819. TOLY original_lyricist
  2820. TOPE original_artist
  2821. TOR original_year
  2822. TORY original_year
  2823. TOT original_album
  2824. TOWN file_owner
  2825. TP1 artist
  2826. TP2 band
  2827. TP3 conductor
  2828. TP4 remixer
  2829. TPA part_of_a_set
  2830. TPB publisher
  2831. TPE1 artist
  2832. TPE2 band
  2833. TPE3 conductor
  2834. TPE4 remixer
  2835. TPOS part_of_a_set
  2836. TPRO produced_notice
  2837. TPUB publisher
  2838. TRC isrc
  2839. TRCK track_number
  2840. TRD recording_dates
  2841. TRDA recording_dates
  2842. TRK track_number
  2843. TRSN internet_radio_station_name
  2844. TRSO internet_radio_station_owner
  2845. TS2 album_artist_sort_order
  2846. TSA album_sort_order
  2847. TSC composer_sort_order
  2848. TSI size
  2849. TSIZ size
  2850. TSO2 album_artist_sort_order
  2851. TSOA album_sort_order
  2852. TSOC composer_sort_order
  2853. TSOP performer_sort_order
  2854. TSOT title_sort_order
  2855. TSP performer_sort_order
  2856. TSRC isrc
  2857. TSS encoder_settings
  2858. TSSE encoder_settings
  2859. TSST set_subtitle
  2860. TST title_sort_order
  2861. TT1 description
  2862. TT2 title
  2863. TT3 subtitle
  2864. TXT lyricist
  2865. TXX text
  2866. TXXX text
  2867. TYE year
  2868. TYER year
  2869. UFI unique_file_identifier
  2870. UFID unique_file_identifier
  2871. ULT unsychronised_lyric
  2872. USER terms_of_use
  2873. USLT unsynchronised_lyric
  2874. WAF url_file
  2875. WAR url_artist
  2876. WAS url_source
  2877. WCM commercial_information
  2878. WCOM commercial_information
  2879. WCOP copyright
  2880. WCP copyright
  2881. WOAF url_file
  2882. WOAR url_artist
  2883. WOAS url_source
  2884. WORS url_station
  2885. WPAY url_payment
  2886. WPB url_publisher
  2887. WPUB url_publisher
  2888. WXX url_user
  2889. WXXX url_user
  2890. TFEA featured_artist
  2891. TSTU recording_studio
  2892. rgad replay_gain_adjustment
  2893. */
  2894. return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
  2895. }
  2896. static function TextEncodingTerminatorLookup($encoding) {
  2897. // http://www.id3.org/id3v2.4.0-structure.txt
  2898. // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
  2899. static $TextEncodingTerminatorLookup = array(
  2900. 0 => "\x00", // $00 ISO-8859-1. Terminated with $00.
  2901. 1 => "\x00\x00", // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
  2902. 2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
  2903. 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
  2904. 255 => "\x00\x00"
  2905. );
  2906. return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : '');
  2907. }
  2908. static function TextEncodingNameLookup($encoding) {
  2909. // http://www.id3.org/id3v2.4.0-structure.txt
  2910. // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
  2911. static $TextEncodingNameLookup = array(
  2912. 0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00.
  2913. 1 => 'UTF-16', // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
  2914. 2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
  2915. 3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00.
  2916. 255 => 'UTF-16BE'
  2917. );
  2918. return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
  2919. }
  2920. static function IsValidID3v2FrameName($framename, $id3v2majorversion) {
  2921. switch ($id3v2majorversion) {
  2922. case 2:
  2923. return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
  2924. break;
  2925. case 3:
  2926. case 4:
  2927. return preg_match('#[A-Z][A-Z0-9]{3}#', $framename);
  2928. break;
  2929. }
  2930. return false;
  2931. }
  2932. static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
  2933. for ($i = 0; $i < strlen($numberstring); $i++) {
  2934. if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
  2935. if (($numberstring{$i} == '.') && $allowdecimal) {
  2936. // allowed
  2937. } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
  2938. // allowed
  2939. } else {
  2940. return false;
  2941. }
  2942. }
  2943. }
  2944. return true;
  2945. }
  2946. static function IsValidDateStampString($datestamp) {
  2947. if (strlen($datestamp) != 8) {
  2948. return false;
  2949. }
  2950. if (!self::IsANumber($datestamp, false)) {
  2951. return false;
  2952. }
  2953. $year = substr($datestamp, 0, 4);
  2954. $month = substr($datestamp, 4, 2);
  2955. $day = substr($datestamp, 6, 2);
  2956. if (($year == 0) || ($month == 0) || ($day == 0)) {
  2957. return false;
  2958. }
  2959. if ($month > 12) {
  2960. return false;
  2961. }
  2962. if ($day > 31) {
  2963. return false;
  2964. }
  2965. if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) {
  2966. return false;
  2967. }
  2968. if (($day > 29) && ($month == 2)) {
  2969. return false;
  2970. }
  2971. return true;
  2972. }
  2973. static function ID3v2HeaderLength($majorversion) {
  2974. return (($majorversion == 2) ? 6 : 10);
  2975. }
  2976. }
  2977. ?>