PageRenderTime 79ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/sys/plugins/id3/getid3/module.tag.id3v2.php

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