PageRenderTime 38ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/public/library/xinha/plugins/MootoolsFileManager/mootools-filemanager/Backend/Assets/getid3/module.tag.id3v2.php

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