You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1507 lines
61 KiB

  1. <?php
  2. namespace OrpheusNET\Logchecker;
  3. use Symfony\Component\Yaml\Yaml;
  4. use Symfony\Component\Yaml\Exception\ParseException;
  5. /*******************************************************************
  6. * Automated EAC/XLD log checker *
  7. ********************************************************************/
  8. class Logchecker {
  9. var $Log = '';
  10. var $LogPath = null;
  11. var $Logs = array();
  12. var $Tracks = array();
  13. var $Checksum = true;
  14. var $Score = 100;
  15. var $Details = array();
  16. var $Offsets = array();
  17. var $DriveFound = false;
  18. var $AllDrives = [];
  19. var $Drives = [];
  20. var $Drive = null;
  21. var $SecureMode = true;
  22. var $NonSecureMode = null;
  23. var $BadTrack = array();
  24. var $DecreaseScoreTrack = 0;
  25. var $RIPPER = null;
  26. var $Language = null;
  27. var $Version = null;
  28. var $TrackNumber = null;
  29. var $ARTracks = array();
  30. var $Combined = null;
  31. var $CurrLog = null;
  32. var $DecreaseBoost = 0;
  33. var $Range = null;
  34. var $ARSummary = null;
  35. var $XLDSecureRipper = false;
  36. var $Limit = 15; //display low prior msg up to this count
  37. var $LBA = array();
  38. var $FrameReRipConf = array();
  39. var $IARTracks = array();
  40. var $InvalidateCache = true;
  41. var $DubiousTracks = 0;
  42. var $EAC_LANG = array();
  43. var $Chardet = null;
  44. var $FakeDrives = [
  45. 'Generic DVD-ROM SCSI CdRom Device'
  46. ];
  47. var $ValidateChecksum = true;
  48. public function __construct() {
  49. $this->EAC_LANG = require_once(__DIR__ . '/eac_languages.php');
  50. try {
  51. $this->Chardet = new Chardet();
  52. }
  53. catch (\Exception $exc) {
  54. // Could not find chardet
  55. $this->Chardet = null;
  56. }
  57. $this->AllDrives = array_map(function($elem) { return explode(',', $elem); }, file(__DIR__.'/offsets.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES));
  58. }
  59. public function get_log() {
  60. return $this->Log;
  61. }
  62. /**
  63. * @param string $LogPath path to log file on local filesystem
  64. */
  65. function new_file($LogPath) {
  66. $this->reset();
  67. $this->LogPath = $LogPath;
  68. $this->Log = file_get_contents($this->LogPath);
  69. }
  70. function reset() {
  71. $this->LogPath = null;
  72. $this->Logs = array();
  73. $this->Tracks = array();
  74. $this->Checksum = true;
  75. $this->Score = 100;
  76. $this->Details = array();
  77. $this->Offsets = array();
  78. $this->DriveFound = false;
  79. $this->Drives = array();
  80. $this->Drive = null;
  81. $this->SecureMode = true;
  82. $this->NonSecureMode = null;
  83. $this->BadTrack = array();
  84. $this->DecreaseScoreTrack = 0;
  85. $this->RIPPER = null;
  86. $this->Language = null;
  87. $this->Version = null;
  88. $this->TrackNumber = null;
  89. $this->ARTracks = array();
  90. $this->Combined = null;
  91. $this->CurrLog = null;
  92. $this->DecreaseBoost = 0;
  93. $this->Range = null;
  94. $this->ARSummary = null;
  95. $this->XLDSecureRipper = false;
  96. $this->Limit = 15;
  97. $this->LBA = array();
  98. $this->FrameReRipConf = array();
  99. $this->IARTracks = array();
  100. $this->InvalidateCache = true;
  101. $this->DubiousTracks = 0;
  102. }
  103. function validateChecksum($Bool) {
  104. $this->ValidateChecksum = $Bool;
  105. }
  106. private function convert_encoding() {
  107. // Whipper uses UTF-8 so we don't need to bother checking, especially as it's
  108. // possible a log may be falsely detected as a different encoding by chardet
  109. if (strpos($this->Log, "Log created by: whipper") !== false) {
  110. return;
  111. }
  112. // To parse the log, we want to deal with the log in UTF-8. EAC by default should
  113. // always output to UTF-16 and XLD to UTF-8, but sometimes people view the log and
  114. // re-encode them to something else (like Windows-1251), and we need to use chardet
  115. // to detect this so we can then convert it to UTF-8.
  116. if (ord($this->Log[0]) . ord($this->Log[1]) == 0xFF . 0xFE) {
  117. $this->Log = mb_convert_encoding(substr($this->Log, 2), 'UTF-8', 'UTF-16LE');
  118. }
  119. elseif (ord($this->Log[0]) . ord($this->Log[1]) == 0xFE . 0xFF) {
  120. $this->Log = mb_convert_encoding(substr($this->Log, 2), 'UTF-8', 'UTF-16BE');
  121. }
  122. elseif (ord($this->Log[0]) == 0xEF && ord($this->Log[1]) == 0xBB && ord($this->Log[2]) == 0xBF) {
  123. $this->Log = substr($this->Log, 3);
  124. }
  125. elseif ($this->Chardet !== null) {
  126. $Results = $this->Chardet->analyze($this->LogPath);
  127. if ($Results['charset'] !== 'utf-8' && $Results['confidence'] > 0.7) {
  128. $this->Log = mb_convert_encoding($this->Log, 'UTF-8', $Results['charset']);
  129. }
  130. }
  131. }
  132. /**
  133. * @return array Returns an array that contains [Score, Details, Checksum, Log]
  134. */
  135. function parse() {
  136. try {
  137. $this->convert_encoding();
  138. }
  139. catch (\Exception $exc) {
  140. $this->Score = 0;
  141. $this->account('Could not detect log encoding, log is corrupt.');
  142. return $this->return_parse();
  143. }
  144. if (strpos($this->Log, "Log created by: whipper") !== false) {
  145. return $this->whipper_parse();
  146. }
  147. else {
  148. return $this->legacy_parse();
  149. }
  150. }
  151. private function whipper_parse() {
  152. // Whipper 0.7.x has an issue where it can produce invalid YAML
  153. // as it hand writes out the values without dealing properly
  154. // with the escaping the output, so we fix that here
  155. $log = preg_replace_callback('/^ (Release|Album): (.+)$/m', function ($match) {
  156. return " {$match[1]}: ".Yaml::dump($match[2]);
  157. }, $this->Log);
  158. try {
  159. $Yaml = Yaml::parse($log);
  160. }
  161. catch (ParseException $exception) {
  162. $this->account('Could not parse whipper log.', 100);
  163. return $this->return_parse();
  164. }
  165. $this->RIPPER = 'whipper';
  166. $this->Version = explode(" ", $Yaml['Log created by'])[1];
  167. // Releases before this used octal numbers for tracks in the log which
  168. // gets messed up in parsing and we lose track data (e.g. tracks 08 and
  169. // 09 get merged into one entry).
  170. if (empty($this->Version) || version_compare('0.7.3', $this->Version) === 1) {
  171. $this->account('Logs must be produced by whipper 0.7.3+', 100);
  172. return $this->return_parse();
  173. }
  174. if (!empty($Yaml['SHA-256 hash'])) {
  175. $Hash = $Yaml['SHA-256 hash'];
  176. $Lines = explode("\n", trim($this->Log));
  177. $Slice = array_slice($Lines, 0, count($Lines)-1);
  178. $this->Checksum = strtolower(hash('sha256', implode("\n", $Slice))) === strtolower($Hash);
  179. unset($Slice);
  180. unset($Lines);
  181. $Class = $this->Checksum ? 'good' : 'bad';
  182. $Yaml['SHA-256 hash'] = "<span class='{$Class}'>{$Hash}</span>";
  183. }
  184. else {
  185. $this->Checksum = false;
  186. }
  187. $Drive = $Yaml['Ripping phase information']['Drive'];
  188. $Offset = $Yaml['Ripping phase information']['Read offset correction'];
  189. if (in_array(trim($Drive), $this->FakeDrives)) {
  190. $this->account('Virtual drive used: ' . $Drive, 20, false, false, false, 20);
  191. $Yaml['Ripping phase information']['Drive'] = "<span class='bad'>{$Drive}</span>";
  192. }
  193. else {
  194. $this->get_drives($Drive);
  195. $DriveClass = 'badish';
  196. if (count($this->Drives) > 0) {
  197. $DriveClass = 'good';
  198. if (in_array((string) $Offset, $this->Offsets)) {
  199. $OffsetClass = 'good';
  200. }
  201. else {
  202. $OffsetClass = 'bad';
  203. $this->account('Incorrect read offset for drive. Correct offsets are: ' . implode(', ', $this->Offsets) . ' (Checked against the following drive(s): ' . implode(', ', $this->Drives) . ')', 5, false, false, false, 5);
  204. }
  205. }
  206. else {
  207. $Drive .= ' (not found in database)';
  208. $OffsetClass = 'badish';
  209. if ($Offset === '0') {
  210. $OffsetClass = 'bad';
  211. $this->account('The drive was not found in the database, so we cannot determine the correct read offset. However, the read offset in this case was 0, which is almost never correct. As such, we are assuming that the offset is incorrect', 5, false, false, false, 5);
  212. }
  213. }
  214. $Yaml['Ripping phase information']['Drive'] = "<span class='{$DriveClass}'>{$Drive}</span>";
  215. $Offset = ($Offset > 0) ? '+' . (string) $Offset : (string) $Offset;
  216. $Yaml['Ripping phase information']['Read offset correction'] = "<span class='{$OffsetClass}'>{$Offset}</span>";
  217. }
  218. $DefeatCache = $Yaml['Ripping phase information']['Defeat audio cache'];
  219. if (is_string($DefeatCache)) {
  220. $Value = (strtolower($DefeatCache) === 'yes') ? 'Yes' : 'No';
  221. $Class = (strtolower($DefeatCache) === 'yes') ? 'good' : 'bad';
  222. }
  223. else {
  224. $Value = ($DefeatCache === true) ? 'true' : 'false';
  225. $Class = ($DefeatCache === true) ? 'good' : 'bad';
  226. }
  227. if ($Class === 'bad') {
  228. $this->account('"Defeat audio cache" should be Yes/true', 10);
  229. }
  230. $Yaml['Ripping phase information']['Defeat audio cache'] = "<span class='{$Class}'>{$Value}</span>";
  231. foreach ($Yaml['Tracks'] as $Key => $Track) {
  232. $Yaml['Tracks'][$Key]['Peak level'] = sprintf('%.6f', $Track['Peak level']);
  233. $Class = 'good';
  234. if ($Track['Test CRC'] !== $Track['Copy CRC']) {
  235. $Class = 'bad';
  236. $this->account("CRC mismatch: {$Track['Test CRC']} and {$Track['Copy CRC']}", 30);
  237. }
  238. $Yaml['Tracks'][$Key]['Test CRC'] = "<span class='{$Class}'>{$Track['Test CRC']}</span>";
  239. $Yaml['Tracks'][$Key]['Copy CRC'] = "<span class='{$Class}'>{$Track['Copy CRC']}</span>";
  240. }
  241. $CreationDate = gmdate("Y-m-d\TH:i:s\Z", $Yaml['Log creation date']);
  242. $this->Log = "Log created by: {$Yaml['Log created by']}\nLog creation date: {$CreationDate}\n\n";
  243. $this->Log .= "Ripping phase information:\n";
  244. foreach ($Yaml['Ripping phase information'] as $Key => $Value) {
  245. if (is_bool($Value)) {
  246. $Value = ($Value) ? 'true' : 'false';
  247. }
  248. $this->Log .= " {$Key}: {$Value}\n";
  249. }
  250. $this->Log .= "\n";
  251. $this->Log .= "CD metadata:\n";
  252. foreach ($Yaml['CD metadata'] as $Key => $Value) {
  253. if (is_bool($Value)) {
  254. $Value = ($Value) ? 'true' : 'false';
  255. }
  256. $this->Log .= " {$Key}: {$Value}\n";
  257. }
  258. $this->Log .= "\n";
  259. $this->Log .= "TOC:\n";
  260. foreach ($Yaml['TOC'] as $Key => $Track) {
  261. $this->Log .= " {$Key}:\n";
  262. foreach ($Track as $KKey => $Value) {
  263. $this->Log .= " {$KKey}: {$Value}\n";
  264. }
  265. $this->Log .= "\n";
  266. }
  267. $this->Log .= "Tracks:\n";
  268. foreach ($Yaml['Tracks'] as $Key => $Track) {
  269. $this->Log .= " {$Key}:\n";
  270. foreach ($Track as $KKey => $Value) {
  271. if (is_array($Value)) {
  272. $this->Log .= " {$KKey}:\n";
  273. foreach ($Value as $KKKey => $VValue) {
  274. $this->Log .= " {$KKKey}: {$VValue}\n";
  275. }
  276. }
  277. else {
  278. if (is_bool($Value)) {
  279. $Value = ($Value) ? 'Yes' : 'No';
  280. }
  281. $this->Log .= " {$KKey}: {$Value}\n";
  282. }
  283. }
  284. $this->Log .= "\n";
  285. }
  286. $this->Log .= "Conclusive status report:\n";
  287. foreach ($Yaml['Conclusive status report'] as $Key => $Value) {
  288. $this->Log .= " {$Key}: {$Value}\n";
  289. }
  290. $this->Log .= "\n";
  291. $this->Log .= "SHA-256 hash: {$Yaml['SHA-256 hash']}\n";
  292. return $this->return_parse();
  293. }
  294. private function legacy_parse() {
  295. foreach ($this->EAC_LANG as $Lang => $Dict) {
  296. if ($Lang === 'en') {
  297. continue;
  298. }
  299. if (preg_match('/'.preg_quote($Dict[1274], "/").'/ui', $this->Log) === 1) {
  300. $this->account("Translated log from {$Dict[1]} ({$Dict[2]}) to {$this->EAC_LANG['en'][1]}.", false, false, false, true);
  301. foreach ($Dict as $Key => $Value) {
  302. if (empty($this->EAC_LANG['en'][$Key])) {
  303. continue;
  304. }
  305. if (!is_array($Value)) {
  306. $Value = [$Value];
  307. }
  308. foreach ($Value as $VValue) {
  309. $Log = preg_replace('/'.preg_quote($VValue, '/').'/ui', $this->EAC_LANG['en'][$Key], $this->Log);
  310. if ($Log !== null) {
  311. $this->Log = $Log;
  312. }
  313. }
  314. }
  315. break;
  316. }
  317. }
  318. $this->Log = str_replace(array("\r\n", "\r"), array("\n", ""), $this->Log);
  319. // Split the log apart
  320. if (preg_match("/[\=]+\s+Log checksum/i", $this->Log)) { // eac checksum
  321. $this->Logs = preg_split("/(\n\=+\s+Log checksum.*)/i", $this->Log, -1, PREG_SPLIT_DELIM_CAPTURE);
  322. } elseif (preg_match("/[\-]+BEGIN XLD SIGNATURE[\S\n\-]+END XLD SIGNATURE[\-]+/i", $this->Log)) { // xld checksum (plugin)
  323. $this->Logs = preg_split("/(\n[\-]+BEGIN XLD SIGNATURE[\S\n\-]+END XLD SIGNATURE[\-]+)/i", $this->Log, -1, PREG_SPLIT_DELIM_CAPTURE);
  324. } else { //no checksum
  325. $this->Checksum = false;
  326. $this->Logs = preg_split("/(\nEnd of status report)/i", $this->Log, -1, PREG_SPLIT_DELIM_CAPTURE);
  327. foreach ($this->Logs as $Key => $Value) {
  328. if (preg_match("/---- CUETools DB Plugin V.+/i", $Value)) {
  329. unset($this->Logs[$Key]);
  330. }
  331. }
  332. }
  333. foreach ($this->Logs as $Key => $Log) {
  334. $Log = trim($Log);
  335. if ($Log === "" || preg_match('/^\-+$/i', $Log)) {
  336. unset($this->Logs[$Key]);
  337. } //strip empty
  338. //append stat msgs
  339. elseif (!$this->Checksum && preg_match("/End of status report/i", $Log)) {
  340. $this->Logs[$Key - 1] .= $Log;
  341. unset($this->Logs[$Key]);
  342. } elseif ($this->Checksum && preg_match("/[\=]+\s+Log checksum/i", $Log)) {
  343. $this->Logs[$Key - 1] .= $Log;
  344. unset($this->Logs[$Key]);
  345. } elseif ($this->Checksum && preg_match("/[\-]+BEGIN XLD SIGNATURE/i", $Log)) {
  346. $this->Logs[$Key - 1] .= $Log;
  347. unset($this->Logs[$Key]);
  348. }
  349. }
  350. $this->Logs = array_values($this->Logs); //rebuild index
  351. if (count($this->Logs) > 1) {
  352. $this->Combined = count($this->Logs);
  353. } //is_combined
  354. foreach ($this->Logs as $LogArrayKey => $Log) {
  355. $this->CurrLog = $LogArrayKey + 1;
  356. $CurrScore = $this->Score;
  357. $Log = preg_replace('/(\=+\s+Log checksum.*)/i', '<span class="good">$1</span>', $Log, 1, $Count);
  358. if (preg_match('/Exact Audio Copy (.+) from/i', $Log, $Matches)) { //eac v1 & checksum
  359. // we set $this->Checksum to true here as these torrents are already trumpable by virtue of a bad score
  360. if ($Matches[1]) {
  361. $this->Version = floatval(explode(" ", substr($Matches[1], 1))[0]);
  362. if ($this->Version <= 0.95) {
  363. $this->Checksum = false;
  364. $this->account("EAC version older than 0.99", 30);
  365. }
  366. if ($this->Version < 1) {
  367. $this->Checksum = false;
  368. }
  369. elseif ($this->Version >= 1 && $Count) {
  370. $this->Checksum = $this->Checksum && true;
  371. }
  372. else {
  373. // Above version 1 and no checksum
  374. $this->Checksum = false;
  375. }
  376. }
  377. else {
  378. $this->Checksum = false;
  379. $this->account("EAC version older than 0.99", 30);
  380. }
  381. }
  382. elseif (preg_match('/EAC extraction logfile from/i', $Log)) {
  383. $this->Checksum = false;
  384. $this->account("EAC version older than 0.99", 30);
  385. }
  386. $Log = preg_replace('/([\-]+BEGIN XLD SIGNATURE[\S\n\-]+END XLD SIGNATURE[\-]+)/i', '<span class="good">$1</span>', $Log, 1, $Count);
  387. if (preg_match('/X Lossless Decoder version (\d+) \((.+)\)/i', $Log, $Matches)) { //xld version & checksum
  388. $this->Version = $Matches[1];
  389. if ($this->Version >= 20121222 && !$Count) {
  390. $this->Checksum = false;
  391. //$this->account('No checksum with XLD 20121222 or newer', 15);
  392. }
  393. else {
  394. $this->Checksum = $this->Checksum && true;
  395. }
  396. }
  397. $Log = preg_replace('/Exact Audio Copy (.+) from (.+)/i', 'Exact Audio Copy <span class="log1">$1</span> from <span class="log1">$2</span>', $Log, 1, $Count);
  398. $Log = preg_replace("/EAC extraction logfile from (.+)\n+(.+)/i", "<span class=\"good\">EAC extraction logfile from <span class=\"log5\">$1</span></span>\n\n<span class=\"log4\">$2</span>", $Log, 1, $EAC);
  399. $Log = preg_replace("/X Lossless Decoder version (.+) \((.+)\)/i", "X Lossless Decoder version <span class=\"log1\">$1</span> (<span class=\"log1\">$2</span>)", $Log, 1, $Count);
  400. $Log = preg_replace("/XLD extraction logfile from (.+)\n+(.+)/i", "<span class=\"good\">XLD extraction logfile from <span class=\"log5\">$1</span></span>\n\n<span class=\"log4\">$2</span>", $Log, 1, $XLD);
  401. if (!$EAC && !$XLD) {
  402. if ($this->Combined) {
  403. unset($this->Details);
  404. $this->Details[] = "Combined Log (" . $this->Combined . ")";
  405. $this->Details[] = "Unrecognized log file (" . $this->CurrLog . ")! Feel free to report for manual review.";
  406. } else {
  407. $this->Details[] = "Unrecognized log file! Feel free to report for manual review.";
  408. }
  409. $this->Score = 0;
  410. return $this->return_parse();
  411. } else {
  412. $this->RIPPER = ($EAC) ? "EAC" : "XLD";
  413. }
  414. if ($this->ValidateChecksum && $this->Checksum && !empty($this->LogPath)) {
  415. if ($EAC) {
  416. $Command = 'eac_logchecker';
  417. $BadStrings = ['Log entry has no checksum!', 'Log entry was modified, checksum incorrect!'];
  418. $GoodString = 'Log entry is fine!';
  419. }
  420. else {
  421. $Command = 'xld_logchecker';
  422. $BadStrings = ['Malformed', 'Not a logfile'];
  423. $GoodString = 'OK';
  424. }
  425. if (Util::commandExists($Command)) {
  426. $Out = shell_exec("{$Command} ".escapeshellarg($this->LogPath));
  427. if ($Out == null || Util::strposArray($Out, $BadStrings) !== false || strpos($Out, $GoodString) === false) {
  428. $this->Checksum = false;
  429. }
  430. }
  431. else {
  432. $this->account("Could not find {$Command}, checksum not validated.", false, false, false, true);
  433. }
  434. }
  435. $Log = preg_replace_callback("/Used drive( +): (.+)/i", array(
  436. $this,
  437. 'drive'
  438. ), $Log, 1, $Count);
  439. if (!$Count) {
  440. $this->account('Could not verify used drive', 1);
  441. }
  442. $Log = preg_replace_callback("/Media type( +): (.+)/i", array(
  443. $this,
  444. 'media_type_xld'
  445. ), $Log, 1, $Count);
  446. if ($XLD && $this->Version && $this->Version >= 20130127 && !$Count) {
  447. $this->account('Could not verify media type', 1);
  448. }
  449. $Log = preg_replace_callback('/Read mode( +): ([a-z]+)(.*)?/i', array(
  450. $this,
  451. 'read_mode'
  452. ), $Log, 1, $Count);
  453. if (!$Count && $EAC) {
  454. $this->account('Could not verify read mode', 1);
  455. }
  456. $Log = preg_replace_callback('/Ripper mode( +): (.*)/i', array(
  457. $this,
  458. 'ripper_mode_xld'
  459. ), $Log, 1, $XLDRipperMode);
  460. $Log = preg_replace_callback('/Use cdparanoia mode( +): (.*)/i', array(
  461. $this,
  462. 'cdparanoia_mode_xld'
  463. ), $Log, 1, $XLDCDParanoiaMode);
  464. if (!$XLDRipperMode && !$XLDCDParanoiaMode && $XLD) {
  465. $this->account('Could not verify read mode', 1);
  466. }
  467. $Log = preg_replace_callback('/Max retry count( +): (\d+)/i', array(
  468. $this,
  469. 'max_retry_count'
  470. ), $Log, 1, $Count);
  471. if (!$Count && $XLD) {
  472. $this->account('Could not verify max retry count');
  473. }
  474. $Log = preg_replace_callback('/Utilize accurate stream( +): (Yes|No)/i', array(
  475. $this,
  476. 'accurate_stream'
  477. ), $Log, 1, $EAC_ac_stream);
  478. $Log = preg_replace_callback('/, (|NO )accurate stream/i', array(
  479. $this,
  480. 'accurate_stream_eac_pre99'
  481. ), $Log, 1, $EAC_ac_stream_pre99);
  482. if (!$EAC_ac_stream && !$EAC_ac_stream_pre99 && !$this->NonSecureMode && $EAC) {
  483. $this->account('Could not verify accurate stream', 20);
  484. }
  485. $Log = preg_replace_callback('/Defeat audio cache( +): (Yes|No)/i', array(
  486. $this,
  487. 'defeat_audio_cache'
  488. ), $Log, 1, $EAC_defeat_cache);
  489. $Log = preg_replace_callback('/ (|NO )disable cache/i', array(
  490. $this,
  491. 'defeat_audio_cache_eac_pre99'
  492. ), $Log, 1, $EAC_defeat_cache_pre99);
  493. if (!$EAC_defeat_cache && !$EAC_defeat_cache_pre99 && !$this->NonSecureMode && $EAC) {
  494. $this->account('Could not verify defeat audio cache', 1);
  495. }
  496. $Log = preg_replace_callback('/Disable audio cache( +): (.*)/i', array(
  497. $this,
  498. 'defeat_audio_cache_xld'
  499. ), $Log, 1, $Count);
  500. if (!$Count && $XLD) {
  501. $this->account('Could not verify defeat audio cache', 1);
  502. }
  503. $Log = preg_replace_callback('/Make use of C2 pointers( +): (Yes|No)/i', array(
  504. $this,
  505. 'c2_pointers'
  506. ), $Log, 1, $C2);
  507. $Log = preg_replace_callback('/with (|NO )C2/i', array(
  508. $this,
  509. 'c2_pointers_eac_pre99'
  510. ), $Log, 1, $C2_EACpre99);
  511. if (!$C2 && !$C2_EACpre99 && !$this->NonSecureMode) {
  512. $this->account('Could not verify C2 pointers', 1);
  513. }
  514. $Log = preg_replace_callback('/Read offset correction( +): ([+-]?[0-9]+)/i', array(
  515. $this,
  516. 'read_offset'
  517. ), $Log, 1, $Count);
  518. if (!$Count) {
  519. $this->account('Could not verify read offset', 1);
  520. }
  521. $Log = preg_replace("/(Combined read\/write offset correction\s+:\s+\d+)/i", "<span class=\"bad\">$1</span>", $Log, 1, $Count);
  522. if ($Count) {
  523. $this->account('Combined read/write offset cannot be verified', 4, false, false, false, 4);
  524. }
  525. //xld alternate offset table
  526. $Log = preg_replace("/(List of \w+ offset correction values) *(\n+)(( *.*confidence .*\) ?\n)+)/i", "<span class=\"log5\">$1</span>$2<span class=\"log4\">$3</span>\n", $Log, 1, $Count);
  527. $Log = preg_replace("/(List of \w+ offset correction values) *\n( *\# +\| +Absolute +\| +Relative +\| +Confidence) *\n( *\-+) *\n(( *\d+ +\| +\-?\+?\d+ +\| +\-?\+?\d+ +\| +\d+ *\n)+)/i", "<span class=\"log5\">$1</span>\n<span class=\"log4\">$2\n$3\n$4\n</span>", $Log, 1, $Count);
  528. $Log = preg_replace('/Overread into Lead-In and Lead-Out( +): (Yes|No)/i', '<span class="log5">Overread into Lead-In and Lead-Out$1</span>: <span class="log4">$2</span>', $Log, 1, $Count);
  529. $Log = preg_replace_callback('/Fill up missing offset samples with silence( +): (Yes|No)/i', array(
  530. $this,
  531. 'fill_offset_samples'
  532. ), $Log, 1, $Count);
  533. if (!$Count && $EAC) {
  534. $this->account('Could not verify missing offset samples', 1);
  535. }
  536. $Log = preg_replace_callback('/Delete leading and trailing silent blocks([ \w]*)( +): (Yes|No)/i', array(
  537. $this,
  538. 'delete_silent_blocks'
  539. ), $Log, 1, $Count);
  540. if (!$Count && $EAC) {
  541. $this->account('Could not verify silent blocks', 1);
  542. }
  543. $Log = preg_replace_callback('/Null samples used in CRC calculations( +): (Yes|No)/i', array(
  544. $this,
  545. 'null_samples'
  546. ), $Log, 1, $Count);
  547. if (!$Count && $EAC) {
  548. $this->account('Could not verify null samples');
  549. }
  550. $Log = preg_replace('/Used interface( +): ([^\n]+)/i', '<span class="log5">Used interface$1</span>: <span class="log4">$2</span>', $Log, 1, $Count);
  551. $Log = preg_replace_callback('/Gap handling( +): ([^\n]+)/i', array(
  552. $this,
  553. 'gap_handling'
  554. ), $Log, 1, $Count);
  555. if (!$Count && $EAC) {
  556. $this->account('Could not verify gap handling', 10);
  557. }
  558. $Log = preg_replace_callback('/Gap status( +): (.*)/i', array(
  559. $this,
  560. 'gap_handling_xld'
  561. ), $Log, 1, $Count);
  562. if (!$Count && $XLD) {
  563. $this->account('Could not verify gap status', 10);
  564. }
  565. $Log = preg_replace('/Used output format( +): ([^\n]+)/i', '<span class="log5">Used output format$1</span>: <span class="log4">$2</span>', $Log, 1, $Count);
  566. $Log = preg_replace('/Sample format( +): ([^\n]+)/i', '<span class="log5">Sample format$1</span>: <span class="log4">$2</span>', $Log, 1, $Count);
  567. $Log = preg_replace('/Selected bitrate( +): ([^\n]+)/i', '<span class="log5">Selected bitrate$1</span>: <span class="log4">$2</span>', $Log, 1, $Count);
  568. $Log = preg_replace('/( +)(\d+ kBit\/s)/i', '<span>$1</span><span class="log4">$2</span>', $Log, 1, $Count);
  569. $Log = preg_replace('/Quality( +): ([^\n]+)/i', '<span class="log5">Quality$1</span>: <span class="log4">$2</span>', $Log, 1, $Count);
  570. $Log = preg_replace_callback('/Add ID3 tag( +): (Yes|No)/i', array(
  571. $this,
  572. 'add_id3_tag'
  573. ), $Log, 1, $Count);
  574. if (!$Count && $EAC) {
  575. $this->account('Could not verify id3 tag setting', 1);
  576. }
  577. $Log = preg_replace("/(Use compression offset\s+:\s+\d+)/i", "<span class=\"bad\">$1</span>", $Log, 1, $Count);
  578. if ($Count) {
  579. $this->account('Ripped with compression offset', false, 0);
  580. }
  581. $Log = preg_replace('/Command line compressor( +): ([^\n]+)/i', '<span class="log5">Command line compressor$1</span>: <span class="log4">$2</span>', $Log, 1, $Count);
  582. $Log = preg_replace("/Additional command line options([^\n]{70,110} )/", "Additional command line options$1<br>", $Log);
  583. $Log = preg_replace('/( *)Additional command line options( +): (.+)\n/i', '<span class="log5">Additional command line options$2</span>: <span class="log4">$3</span>' . "\n", $Log, 1, $Count);
  584. // xld album gain
  585. $Log = preg_replace("/All Tracks\s*\n(\s*Album gain\s+:) (.*)?\n(\s*Peak\s+:) (.*)?/i", "<span class=\"log5\">All Tracks</span>\n<strong>$1 <span class=\"log3\">$2</span>\n$3 <span class=\"log3\">$4</span></strong>", $Log, 1, $Count);
  586. if (!$Count && $XLD) {
  587. $this->account('Could not verify album gain');
  588. }
  589. // pre-0.99
  590. $Log = preg_replace('/Other options( +):/i', '<span class="log5">Other options$1</span>:', $Log, 1, $Count);
  591. $Log = preg_replace('/\n( *)Native Win32 interface(.+)/i', "\n$1<span class=\"log4\">Native Win32 interface$2</span>", $Log, 1, $Count);
  592. // 0.99
  593. $Log = str_replace('TOC of the extracted CD', '<span class="log4 log5">TOC of the extracted CD</span>', $Log);
  594. $Log = preg_replace('/( +)Track( +)\|( +)Start( +)\|( +)Length( +)\|( +)Start sector( +)\|( +)End sector( ?)/i', '<strong>$0</strong>', $Log);
  595. $Log = preg_replace('/-{10,100}/', '<strong>$0</strong>', $Log);
  596. $Log = preg_replace_callback('/( +)([0-9]{1,3})( +)\|( +)(([0-9]{1,3}:)?[0-9]{2}[\.:][0-9]{2})( +)\|( +)(([0-9]{1,3}:)?[0-9]{2}[\.:][0-9]{2})( +)\|( +)([0-9]{1,10})( +)\|( +)([0-9]{1,10})( +)\n/i', array(
  597. $this,
  598. 'toc'
  599. ), $Log);
  600. $Log = str_replace('None of the tracks are present in the AccurateRip database', '<span class="badish">None of the tracks are present in the AccurateRip database</span>', $Log);
  601. $Log = str_replace('Disc not found in AccurateRip DB.', '<span class="badish">Disc not found in AccurateRip DB.</span>', $Log);
  602. $Log = preg_replace('/No errors occurr?ed/i', '<span class="good">No errors occurred</span>', $Log);
  603. $Log = preg_replace("/(There were errors) ?\n/i", "<span class=\"bad\">$1</span>\n", $Log);
  604. $Log = preg_replace("/(Some inconsistencies found) ?\n/i", "<span class=\"badish\">$1</span>\n", $Log);
  605. $Log = preg_replace('/End of status report/i', '<span class="good">End of status report</span>', $Log);
  606. $Log = preg_replace('/Track(\s*)Ripping Status(\s*)\[Disc ID: ([0-9a-f]{8}-[0-9a-f]{8})\]/i', '<strong>Track</strong>$1<strong>Ripping Status</strong>$2<strong>Disc ID: </strong><span class="log1">$3</span>', $Log);
  607. $Log = preg_replace('/(All Tracks Accurately Ripped\.?)/i', '<span class="good">$1</span>', $Log);
  608. $Log = preg_replace("/\d+ track.* +accurately ripped\.? *\n/i", '<span class="good">$0</span>', $Log);
  609. $Log = preg_replace("/\d+ track.* +not present in the AccurateRip database\.? *\n/i", '<span class="badish">$0</span>', $Log);
  610. $Log = preg_replace("/\d+ track.* +canceled\.? *\n/i", '<span class="bad">$0</span>', $Log);
  611. $Log = preg_replace("/\d+ track.* +could not be verified as accurate\.? *\n/i", '<span class="badish">$0</span>', $Log);
  612. $Log = preg_replace("/Some tracks could not be verified as accurate\.? *\n/i", '<span class="badish">$0</span>', $Log);
  613. $Log = preg_replace("/No tracks could be verified as accurate\.? *\n/i", '<span class="badish">$0</span>', $Log);
  614. $Log = preg_replace("/You may have a different pressing.*\n/i", '<span class="goodish">$0</span>', $Log);
  615. //xld accurip summary
  616. $Log = preg_replace_callback("/(Track +\d+ +: +)(OK +)\(A?R?\d?,? ?confidence +(\d+).*?\)(.*)\n/i", array(
  617. $this,
  618. 'ar_summary_conf_xld'
  619. ), $Log);
  620. $Log = preg_replace_callback("/(Track +\d+ +: +)(NG|Not Found).*?\n/i", array(
  621. $this,
  622. 'ar_summary_conf_xld'
  623. ), $Log);
  624. $Log = preg_replace( //Status line
  625. "/( *.{2} ?)(\d+ track\(s\).*)\n/i", "$1<span class=\"log4\">$2</span>\n", $Log, 1);
  626. //(..) may need additional entries
  627. //accurip summary (range)
  628. $Log = preg_replace("/\n( *AccurateRip summary\.?)/i", "\n<span class=\"log4 log5\">$1</span>", $Log);
  629. $Log = preg_replace_callback("/(Track +\d+ +.*?accurately ripped\.? *)(\(confidence +)(\d+)\)(.*)\n/i", array(
  630. $this,
  631. 'ar_summary_conf'
  632. ), $Log);
  633. $Log = preg_replace("/(Track +\d+ +.*?in database *)\n/i", "<span class=\"badish\">$1</span>\n", $Log, -1, $Count);
  634. if ($Count) {
  635. $this->ARSummary['bad'] = $Count;
  636. }
  637. $Log = preg_replace("/(Track +\d+ +.*?(could not|cannot) be verified as accurate.*)\n/i", "<span class=\"badish\">$1</span>\n", $Log, -1, $Count);
  638. if ($Count) {
  639. $this->ARSummary['bad'] = $Count;
  640. } //don't mind the actual count
  641. //range rip
  642. $Log = preg_replace("/\n( *Selected range)/i", "\n<span class=\"bad\">$1</span>", $Log, 1, $Range1);
  643. $Log = preg_replace('/\n( *Range status and errors)/i', "\n<span class=\"bad\">$1</span>", $Log, 1, $Range2);
  644. if ($Range1 || $Range2) {
  645. $this->Range = 1;
  646. $this->account('Range rip detected', 30);
  647. }
  648. $FormattedTrackListing = '';
  649. //------ Handle individual tracks ------//
  650. if (!$this->Range) {
  651. preg_match('/\nTrack( +)([0-9]{1,3})([^<]+)/i', $Log, $Matches);
  652. $TrackListing = $Matches[0];
  653. $FullTracks = preg_split('/\nTrack( +)([0-9]{1,3})/i', $TrackListing, -1, PREG_SPLIT_DELIM_CAPTURE);
  654. array_shift($FullTracks);
  655. $TrackBodies = preg_split('/\nTrack( +)([0-9]{1,3})/i', $TrackListing, -1);
  656. array_shift($TrackBodies);
  657. //------ Range rip ------//
  658. } else {
  659. preg_match('/\n( +)Filename +(.*)([^<]+)/i', $Log, $Matches);
  660. $TrackListing = $Matches[0];
  661. $FullTracks = preg_split('/\n( +)Filename +(.*)/i', $TrackListing, -1, PREG_SPLIT_DELIM_CAPTURE);
  662. array_shift($FullTracks);
  663. $TrackBodies = preg_split('/\n( +)Filename +(.*)/i', $TrackListing, -1);
  664. array_shift($TrackBodies);
  665. }
  666. $Tracks = array();
  667. foreach ($TrackBodies as $Key => $TrackBody) {
  668. // The number of spaces between 'Track' and the number, to keep formatting intact
  669. $Spaces = $FullTracks[($Key * 3)];
  670. // Track number
  671. $TrackNumber = $FullTracks[($Key * 3) + 1];
  672. $this->TrackNumber = $TrackNumber;
  673. // How much to decrease the overall score by, if this track fails and no attempt at recovery is made later on
  674. $this->DecreaseScoreTrack = 0;
  675. // List of things that went wrong to add to $this->Bad if this track fails and no attempt at recovery is made later on
  676. $this->BadTrack = array();
  677. // The track number is stripped in the preg_split, let's bring it back, eh?
  678. if (!$this->Range) {
  679. $TrackBody = '<span class="log5">Track</span>' . $Spaces . '<span class="log4 log1">' . $TrackNumber . '</span>' . $TrackBody;
  680. } else {
  681. $TrackBody = $Spaces . '<span class="log5">Filename</span> <span class="log4 log3">' . $TrackNumber . '</span>' . $TrackBody;
  682. }
  683. $TrackBody = preg_replace('/Filename ((.+)?\.(wav|flac|ape))\n/is', /* match newline for xld multifile encodes */ "<span class=\"log4\">Filename <span class=\"log3\">$1</span></span>\n", $TrackBody, -1, $Count);
  684. if (!$Count && !$this->Range) {
  685. $this->account_track('Could not verify filename', 1);
  686. }
  687. // xld track gain
  688. $TrackBody = preg_replace("/( *Track gain\s+:) (.*)?\n(\s*Peak\s+:) (.*)?/i", "<strong>$1 <span class=\"log3\">$2</span>\n$3 <span class=\"log3\">$4</span></strong>", $TrackBody, -1, $Count);
  689. $TrackBody = preg_replace('/( +)(Statistics *)\n/i', "$1<span class=\"log5\">$2</span>\n", $TrackBody, -1, $Count);
  690. $TrackBody = preg_replace_callback('/(Read error)( +:) (\d+)/i', array(
  691. $this,
  692. 'xld_stat'
  693. ), $TrackBody, -1, $Count);
  694. if (!$Count && $XLD) {
  695. $this->account_track('Could not verify read errors');
  696. }
  697. $TrackBody = preg_replace_callback('/(Skipped \(treated as error\))( +:) (\d+)/i', array(
  698. $this,
  699. 'xld_stat'
  700. ), $TrackBody, -1, $Count);
  701. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  702. $this->account_track('Could not verify skipped errors');
  703. }
  704. $TrackBody = preg_replace_callback('/(Edge jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  705. $this,
  706. 'xld_stat'
  707. ), $TrackBody, -1, $Count);
  708. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  709. $this->account_track('Could not verify edge jitter errors');
  710. }
  711. $TrackBody = preg_replace_callback('/(Atom jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  712. $this,
  713. 'xld_stat'
  714. ), $TrackBody, -1, $Count);
  715. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  716. $this->account_track('Could not verify atom jitter errors');
  717. }
  718. $TrackBody = preg_replace_callback( //xld secure ripper
  719. '/(Jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  720. $this,
  721. 'xld_stat'
  722. ), $TrackBody, -1, $Count);
  723. if (!$Count && $XLD && $this->XLDSecureRipper) {
  724. $this->account_track('Could not verify jitter errors');
  725. }
  726. $TrackBody = preg_replace_callback( //xld secure ripper
  727. '/(Retry sector count)( +:) (\d+)/i', array(
  728. $this,
  729. 'xld_stat'
  730. ), $TrackBody, -1, $Count);
  731. if (!$Count && $XLD && $this->XLDSecureRipper) {
  732. $this->account_track('Could not verify retry sector count');
  733. }
  734. $TrackBody = preg_replace_callback( //xld secure ripper
  735. '/(Damaged sector count)( +:) (\d+)/i', array(
  736. $this,
  737. 'xld_stat'
  738. ), $TrackBody, -1, $Count);
  739. if (!$Count && $XLD && $this->XLDSecureRipper) {
  740. $this->account_track('Could not verify damaged sector count');
  741. }
  742. $TrackBody = preg_replace_callback('/(Drift error \(maybe fixed\))( +:) (\d+)/i', array(
  743. $this,
  744. 'xld_stat'
  745. ), $TrackBody, -1, $Count);
  746. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  747. $this->account_track('Could not verify drift errors');
  748. }
  749. $TrackBody = preg_replace_callback('/(Dropped bytes error \(maybe fixed\))( +:) (\d+)/i', array(
  750. $this,
  751. 'xld_stat'
  752. ), $TrackBody, -1, $Count);
  753. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  754. $this->account_track('Could not verify dropped bytes errors');
  755. }
  756. $TrackBody = preg_replace_callback('/(Duplicated bytes error \(maybe fixed\))( +:) (\d+)/i', array(
  757. $this,
  758. 'xld_stat'
  759. ), $TrackBody, -1, $Count);
  760. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  761. $this->account_track('Could not verify duplicated bytes errors');
  762. }
  763. $TrackBody = preg_replace_callback('/(Inconsistency in error sectors)( +:) (\d+)/i', array(
  764. $this,
  765. 'xld_stat'
  766. ), $TrackBody, -1, $Count);
  767. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  768. $this->account_track('Could not verify inconsistent error sectors');
  769. }
  770. $TrackBody = preg_replace("/(List of suspicious positions +)(: *\n?)(( *.* +\d{2}:\d{2}:\d{2} *\n)+)/i", '<span class="bad">$1</span><strong>$2</strong><span class="bad">$3</span></span>', $TrackBody, -1, $Count);
  771. if ($Count) {
  772. $this->account_track('Suspicious position(s) found', 20);
  773. }
  774. $TrackBody = preg_replace('/Suspicious position( +)([0-9]:[0-9]{2}:[0-9]{2})/i', '<span class="bad">Suspicious position$1<span class="log4">$2</span></span>', $TrackBody, -1, $Count);
  775. if ($Count) {
  776. $this->account_track('Suspicious position(s) found', 20);
  777. }
  778. $TrackBody = preg_replace('/Timing problem( +)([0-9]:[0-9]{2}:[0-9]{2})/i', '<span class="bad">Timing problem$1<span class="log4">$2</span></span>', $TrackBody, -1, $Count);
  779. if ($Count) {
  780. $this->account_track('Timing problem(s) found', 20);
  781. }
  782. $TrackBody = preg_replace('/Missing samples/i', '<span class="bad">Missing samples</span>', $TrackBody, -1, $Count);
  783. if ($Count) {
  784. $this->account_track('Missing sample(s) found', 20);
  785. }
  786. $TrackBody = preg_replace('/Copy aborted/i', '<span class="bad">Copy aborted</span>', $TrackBody, -1, $Count);
  787. if ($Count) {
  788. $Aborted = true;
  789. $this->account_track('Copy aborted', 100);
  790. } else {
  791. $Aborted = false;
  792. }
  793. $TrackBody = preg_replace('/Pre-gap length( +|\s+:\s+)([0-9]{1,2}:[0-9]{2}:[0-9]{2}.?[0-9]{0,2})/i', '<span class="log4">Pre-gap length$1<span class="log3">$2</span></span>', $TrackBody, -1, $Count);
  794. $TrackBody = preg_replace('/Peak level ([0-9]{1,3}\.[0-9] %)/i', '<span class="log4">Peak level <span class="log3">$1</span></span>', $TrackBody, -1, $Count);
  795. $TrackBody = preg_replace('/Extraction speed ([0-9]{1,3}\.[0-9]{1,} X)/i', '<span class="log4">Extraction speed <span class="log3">$1</span></span>', $TrackBody, -1, $Count);
  796. $TrackBody = preg_replace('/Track quality ([0-9]{1,3}\.[0-9] %)/i', '<span class="log4">Track quality <span class="log3">$1</span></span>', $TrackBody, -1, $Count);
  797. $TrackBody = preg_replace('/Range quality ([0-9]{1,3}\.[0-9] %)/i', '<span class="log4">Range quality <span class="log3">$1</span></span>', $TrackBody, -1, $Count);
  798. $TrackBody = preg_replace('/CRC32 hash \(skip zero\)(\s*:) ([0-9A-F]{8})/i', '<span class="log4">CRC32 hash (skip zero)$1<span class="log3"> $2</span></span>', $TrackBody, -1, $Count);
  799. $TrackBody = preg_replace_callback('/Test CRC ([0-9A-F]{8})\n(\s*)Copy CRC ([0-9A-F]{8})/i', array(
  800. $this,
  801. 'test_copy'
  802. ), $TrackBody, -1, $EACTC);
  803. $TrackBody = preg_replace_callback('/CRC32 hash \(test run\)(\s*:) ([0-9A-F]{8})\n(\s*)CRC32 hash(\s+:) ([0-9A-F]{8})/i', array(
  804. $this,
  805. 'test_copy'
  806. ), $TrackBody, -1, $XLDTC);
  807. if (!$EACTC && !$XLDTC && !$Aborted) {
  808. $this->account('Test and copy was not used', 10);
  809. if (!$this->SecureMode) {
  810. if ($EAC) {
  811. $Msg = 'Rip was not done in Secure mode, and T+C was not used - as a result, we cannot verify the authenticity of the rip (-40 points)';
  812. } else if ($XLD) {
  813. $Msg = 'Rip was not done with Secure Ripper / in CDParanoia mode, and T+C was not used - as a result, we cannot verify the authenticity of the rip (-40 points)';
  814. }
  815. if (!in_array($Msg, $this->Details)) {
  816. $this->Score -= 40;
  817. $this->Details[] = $Msg;
  818. }
  819. }
  820. }
  821. $TrackBody = preg_replace('/Copy CRC ([0-9A-F]{8})/i', '<span class="log4">Copy CRC <span class="log3">$1</span></span>', $TrackBody, -1, $Count);
  822. $TrackBody = preg_replace('/CRC32 hash(\s*:) ([0-9A-F]{8})/i', '<span class="log4">CRC32 hash$1<span class="goodish"> $2</span></span>', $TrackBody, -1, $Count);
  823. $TrackBody = str_replace('Track not present in AccurateRip database', '<span class="badish">Track not present in AccurateRip database</span>', $TrackBody);
  824. $TrackBody = preg_replace('/Accurately ripped( +)\(confidence ([0-9]+)\)( +)(\[[0-9A-F]{8}\])/i', '<span class="good">Accurately ripped$1(confidence $2)$3$4</span>', $TrackBody, -1, $Count);
  825. $TrackBody = preg_replace("/Cannot be verified as accurate +\(.*/i", '<span class="badish">$0</span>', $TrackBody, -1, $Count);
  826. //xld ar
  827. $TrackBody = preg_replace_callback('/AccurateRip signature( +): ([0-9A-F]{8})\n(.*?)(Accurately ripped\!?)( +\(A?R?\d?,? ?confidence )([0-9]+\))/i', array(
  828. $this,
  829. 'ar_xld'
  830. ), $TrackBody, -1, $Count);
  831. $TrackBody = preg_replace('/AccurateRip signature( +): ([0-9A-F]{8})\n(.*?)(Rip may not be accurate\.?)(.*?)/i', "<span class=\"log4\">AccurateRip signature$1: <span class=\"badish\">$2</span></span>\n$3<span class=\"badish\">$4$5</span>", $TrackBody, -1, $Count);
  832. $TrackBody = preg_replace('/(Rip may not be accurate\.?)(.*?)/i', "<span class=\"badish\">$1$2</span>", $TrackBody, -1, $Count);
  833. $TrackBody = preg_replace('/AccurateRip signature( +): ([0-9A-F]{8})\n(.*?)(Track not present in AccurateRip database\.?)(.*?)/i', "<span class=\"log4\">AccurateRip signature$1: <span class=\"badish\">$2</span></span>\n$3<span class=\"badish\">$4$5</span>", $TrackBody, -1, $Count);
  834. $TrackBody = preg_replace("/\(matched[ \w]+;\n *calculated[ \w]+;\n[ \w]+signature[ \w:]+\)/i", "<span class=\"goodish\">$0</span>", $TrackBody, -1, $Count);
  835. //ar track + conf
  836. preg_match('/Accurately ripped\!? +\(A?R?\d?,? ?confidence ([0-9]+)\)/i', $TrackBody, $matches);
  837. if ($matches) {
  838. $this->ARTracks[$TrackNumber] = $matches[1];
  839. } else {
  840. $this->ARTracks[$TrackNumber] = 0;
  841. } //no match - no boost
  842. $TrackBody = str_replace('Copy finished', '<span class="log3">Copy finished</span>', $TrackBody);
  843. $TrackBody = preg_replace('/Copy OK/i', '<span class="good">Copy OK</span>', $TrackBody, -1, $Count);
  844. $Tracks[$TrackNumber] = array(
  845. 'number' => $TrackNumber,
  846. 'spaces' => $Spaces,
  847. 'text' => $TrackBody,
  848. 'decreasescore' => $this->DecreaseScoreTrack,
  849. 'bad' => $this->BadTrack
  850. );
  851. $FormattedTrackListing .= "\n" . $TrackBody;
  852. $this->Tracks[$TrackNumber] = $Tracks[$TrackNumber];
  853. }
  854. unset($Tracks);
  855. $Log = str_replace($TrackListing, $FormattedTrackListing, $Log);
  856. $Log = str_replace('<br>', "\n", $Log);
  857. //xld all tracks statistics
  858. $Log = preg_replace('/( +)?(All tracks *)\n/i', "$1<span class=\"log5\">$2</span>\n", $Log, 1);
  859. $Log = preg_replace('/( +)(Statistics *)\n/i', "$1<span class=\"log5\">$2</span>\n", $Log, 1);
  860. $Log = preg_replace_callback('/(Read error)( +:) (\d+)/i', array(
  861. $this,
  862. 'xld_all_stat'
  863. ), $Log, 1);
  864. $Log = preg_replace_callback('/(Skipped \(treated as error\))( +:) (\d+)/i', array(
  865. $this,
  866. 'xld_all_stat'
  867. ), $Log, 1);
  868. $Log = preg_replace_callback('/(Jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  869. $this,
  870. 'xld_all_stat'
  871. ), $Log, 1);
  872. $Log = preg_replace_callback('/(Edge jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  873. $this,
  874. 'xld_all_stat'
  875. ), $Log, 1);
  876. $Log = preg_replace_callback('/(Atom jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  877. $this,
  878. 'xld_all_stat'
  879. ), $Log, 1);
  880. $Log = preg_replace_callback('/(Drift error \(maybe fixed\))( +:) (\d+)/i', array(
  881. $this,
  882. 'xld_all_stat'
  883. ), $Log, 1);
  884. $Log = preg_replace_callback('/(Dropped bytes error \(maybe fixed\))( +:) (\d+)/i', array(
  885. $this,
  886. 'xld_all_stat'
  887. ), $Log, 1);
  888. $Log = preg_replace_callback('/(Duplicated bytes error \(maybe fixed\))( +:) (\d+)/i', array(
  889. $this,
  890. 'xld_all_stat'
  891. ), $Log, 1);
  892. $Log = preg_replace_callback('/(Retry sector count)( +:) (\d+)/i', array(
  893. $this,
  894. 'xld_all_stat'
  895. ), $Log, 1);
  896. $Log = preg_replace_callback('/(Damaged sector count)( +:) (\d+)/i', array(
  897. $this,
  898. 'xld_all_stat'
  899. ), $Log, 1);
  900. //end xld all tracks statistics
  901. $this->Logs[$LogArrayKey] = $Log;
  902. $this->check_tracks();
  903. foreach ($this->Tracks as $Track) { //send score/bad
  904. if ($Track['decreasescore']) {
  905. $this->Score -= $Track['decreasescore'];
  906. }
  907. if (count($Track['bad']) > 0) {
  908. $this->Details = array_merge($this->Details, $Track['bad']);
  909. }
  910. }
  911. unset($this->Tracks); //fixes weird bug
  912. if ($this->NonSecureMode) { #non-secure mode
  913. $this->account($this->NonSecureMode . ' mode was used', 20);
  914. }
  915. if (false && $this->Score != 100) { //boost?
  916. $boost = null;
  917. $minConf = null;
  918. if (!$this->ARSummary) {
  919. foreach ($this->ARTracks as $Track => $Conf) {
  920. if (!is_numeric($Conf) || $Conf < 2) {
  921. $boost = 0;
  922. break;
  923. } //non-ar track found
  924. else {
  925. $boost = 1;
  926. $minConf = (!$minConf || $Conf < $minConf) ? $Conf : $minConf;
  927. }
  928. }
  929. } elseif (isset($this->ARSummary['good'])) { //range with minConf
  930. foreach ($this->ARSummary['good'] as $Track => $Conf) {
  931. if (!is_numeric($Conf)) {
  932. $boost = 0;
  933. break;
  934. } else {
  935. $boost = 1;
  936. $minConf = (!$minConf || $Conf < $minConf) ? $Conf : $minConf;
  937. }
  938. }
  939. if (isset($this->ARSummary['bad']) || isset($this->ARSummary['goodish'])) {
  940. $boost = 0;
  941. } //non-ar track found
  942. }
  943. if ($boost) {
  944. $tmp_score = $this->Score;
  945. $this->Score = (($CurrScore) ? $CurrScore : 100) - $this->DecreaseBoost;
  946. if (((($CurrScore) ? $CurrScore : 100) - $tmp_score) != $this->DecreaseBoost) {
  947. $Msg = 'All tracks accurately ripped with at least confidence ' . $minConf . '. Score ' . (($this->Combined) ? "for log " . $this->CurrLog . " " : '') . 'boosted to ' . $this->Score . ' points!';
  948. $this->Details[] = $Msg;
  949. }
  950. }
  951. }
  952. $this->ARTracks = array();
  953. $this->ARSummary = array();
  954. $this->DecreaseBoost = 0;
  955. $this->SecureMode = true;
  956. $this->NonSecureMode = null;
  957. } //end log loop
  958. $this->Log = implode($this->Logs);
  959. if (strlen($this->Log) === 0) {
  960. $this->Score = 0;
  961. $this->account('Unrecognized log file! Feel free to report for manual review.');
  962. }
  963. $this->Score = ($this->Score < 0) ? 0 : $this->Score; //min. score
  964. if ($this->Combined) {
  965. array_unshift($this->Details, "Combined Log (" . $this->Combined . ")");
  966. } //combined log msg
  967. return $this->return_parse();
  968. }
  969. // Callback functions
  970. function drive($Matches)
  971. {
  972. if (in_array(trim($Matches[2]), $this->FakeDrives)) {
  973. $this->account('Virtual drive used: ' . $Matches[2], 20, false, false, false, 20);
  974. return "<span class=\"log5\">Used Drive$Matches[1]</span>: <span class=\"bad\">$Matches[2]</span>";
  975. }
  976. $DriveName = $Matches[2];
  977. $this->get_drives($DriveName);
  978. if (count($this->Drives) > 0) {
  979. $Class = 'good';
  980. $this->DriveFound = true;
  981. } else {
  982. $Class = 'badish';
  983. $Matches[2] .= ' (not found in database)';
  984. }
  985. return "<span class=\"log5\">Used Drive$Matches[1]</span>: <span class=\"$Class\">$Matches[2]</span>";
  986. }
  987. private function get_drives($DriveName) {
  988. // Necessary transformations to get what the drives report themselves to match up into
  989. // what is from the AccurateRIP DB
  990. $DriveName = str_replace('JLMS', 'Lite-ON', $DriveName);
  991. $DriveName = preg_replace('/TSSTcorp(BD|CD|DVD)/', 'TSSTcorp \1', $DriveName);
  992. $DriveName = preg_replace('/HL-DT-ST(BD|CD|DVD)/', 'HL-DT-ST \1', $DriveName);
  993. $DriveName = str_replace('HL-DT-ST', 'LG Electronics', $DriveName);
  994. $DriveName = str_replace(array('Matshita', 'MATSHITA'), 'Panasonic', $DriveName);
  995. $DriveName = preg_replace('/\s+-\s/', ' ', $DriveName);
  996. $DriveName = preg_replace('/\s+/', ' ', $DriveName);
  997. $DriveName = preg_replace('/\(revision [a-zA-Z0-9\.\,\-]*\)/', '', $DriveName);
  998. $DriveName = preg_replace('/ Adapter.*$/', '', $DriveName);
  999. $DriveName = strtolower($DriveName);
  1000. $MatchedDrives = [];
  1001. for ($i = 0; $i < 5; $i++) {
  1002. $MatchedDrives[$i] = ['drives' => [], 'offsets' => []];
  1003. }
  1004. foreach ($this->AllDrives as list($Drive, $Offset)) {
  1005. $Distance = levenshtein($Drive, $DriveName);
  1006. if ($Distance < 5) {
  1007. $MatchedDrives[$Distance]['drives'][] = $Drive;
  1008. $MatchedDrives[$Distance]['offsets'][] = preg_replace('/[^0-9]/s', '', (string) $Offset);
  1009. }
  1010. }
  1011. foreach ($MatchedDrives as $Match) {
  1012. if (count($Match['drives']) > 0) {
  1013. $this->Drives = $Match['drives'];
  1014. $this->Offsets = $Match['offsets'];
  1015. break;
  1016. }
  1017. }
  1018. }
  1019. function media_type_xld($Matches) {
  1020. // Pressed CD
  1021. if (trim($Matches[2]) == "Pressed CD") {
  1022. $Class = 'good';
  1023. } else { // CD-R etc.; not necessarily "bad" (e.g. commercial CD-R)
  1024. $Class = 'badish';
  1025. $this->account('Not a pressed cd', false, false, true, true);
  1026. }
  1027. return "<span class=\"log5\">Media type$Matches[1]</span>: <span class=\"$Class\">$Matches[2]</span>";
  1028. }
  1029. function read_mode($Matches)
  1030. {
  1031. if ($Matches[2] == 'Secure') {
  1032. $Class = 'good';
  1033. } else {
  1034. $this->SecureMode = false;
  1035. $this->NonSecureMode = $Matches[2];
  1036. $Class = 'bad';
  1037. }
  1038. $Str = '<span class="log5">Read mode' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1039. if ($Matches[3]) {
  1040. $Str .= '<span class="log4">' . $Matches[3] . '</span>';
  1041. }
  1042. return $Str;
  1043. }
  1044. function cdparanoia_mode_xld($Matches)
  1045. {
  1046. if (substr($Matches[2], 0, 3) == 'YES') {
  1047. $Class = 'good';
  1048. } else {
  1049. $this->SecureMode = false;
  1050. $Class = 'bad';
  1051. }
  1052. return '<span class="log5">Use cdparanoia mode' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1053. }
  1054. function ripper_mode_xld($Matches)
  1055. {
  1056. if (substr($Matches[2], 0, 10) == 'CDParanoia') {
  1057. $Class = 'good';
  1058. } elseif ($Matches[2] == "XLD Secure Ripper") {
  1059. $Class = 'good';
  1060. $this->XLDSecureRipper = true;
  1061. } else {
  1062. $this->SecureMode = false;
  1063. $Class = 'bad';
  1064. }
  1065. return '<span class="log5">Ripper mode' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1066. }
  1067. function ar_xld($Matches)
  1068. {
  1069. if (strpos(strtolower($Matches[4]), 'accurately ripped') != -1) {
  1070. $conf = substr($Matches[6], 0, -1);
  1071. if ((int) $conf < 2) {
  1072. $Class = 'goodish';
  1073. } else {
  1074. $Class = 'good';
  1075. }
  1076. } else {
  1077. $Class = 'badish';
  1078. }
  1079. return "<span class=\"log4\">AccurateRip signature$Matches[1]: <span class=\"$Class\">$Matches[2]</span></span>\n$Matches[3]<span class=\"$Class\">$Matches[4]$Matches[5]$Matches[6]</span>";
  1080. }
  1081. function ar_summary_conf_xld($Matches)
  1082. {
  1083. if (strtolower(trim($Matches[2])) == 'ok') {
  1084. if ($Matches[3] < 2) {
  1085. $Class = 'goodish';
  1086. } else {
  1087. $Class = 'good';
  1088. }
  1089. } else {
  1090. $Class = 'badish';
  1091. }
  1092. return "$Matches[1]<span class =\"$Class\">" . substr($Matches[0], strlen($Matches[1])) . "</span>";
  1093. }
  1094. function ar_summary_conf($Matches)
  1095. {
  1096. if ($Matches[3] < 2) {
  1097. $Class = 'goodish';
  1098. $this->ARSummary['goodish'][] = $Matches[3];
  1099. } else {
  1100. $Class = 'good';
  1101. $this->ARSummary['good'][] = $Matches[3];
  1102. }
  1103. return "<span class =\"$Class\">$Matches[0]</span>";
  1104. }
  1105. function max_retry_count($Matches)
  1106. {
  1107. if ($Matches[2] >= 10) {
  1108. $Class = 'goodish';
  1109. } else {
  1110. $Class = 'badish';
  1111. $this->account('Low "max retry count" (potentially bad setting)');
  1112. }
  1113. return '<span class="log5">Max retry count' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1114. }
  1115. function accurate_stream($Matches)
  1116. {
  1117. if ($Matches[2] == 'Yes') {
  1118. $Class = 'good';
  1119. } else {
  1120. $Class = 'bad';
  1121. $this->account('"Utilize accurate stream" should be yes', 20);
  1122. }
  1123. return '<span class="log5">Utilize accurate stream' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1124. }
  1125. function accurate_stream_eac_pre99($Matches)
  1126. {
  1127. if (strtolower($Matches[1]) != 'no ') {
  1128. $Class = 'good';
  1129. } else {
  1130. $Class = 'bad';
  1131. $this->account('"accurate stream" should be yes', 20);
  1132. }
  1133. return ', <span class="' . $Class . '">' . $Matches[1] . 'accurate stream</span>';
  1134. }
  1135. function defeat_audio_cache($Matches)
  1136. {
  1137. if ($Matches[2] == 'Yes') {
  1138. $Class = 'good';
  1139. } else {
  1140. $Class = 'bad';
  1141. $this->account('"Defeat audio cache" should be yes', 10);
  1142. }
  1143. return '<span class="log5">Defeat audio cache' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1144. }
  1145. function defeat_audio_cache_eac_pre99($Matches)
  1146. {
  1147. if (strtolower($Matches[1]) != 'no ') {
  1148. $Class = 'good';
  1149. } else {
  1150. $Class = 'bad';
  1151. $this->account('Audio cache not disabled', 10);
  1152. }
  1153. return '<span> </span><span class="' . $Class . '">' . $Matches[1] . 'disable cache</span>';
  1154. }
  1155. function defeat_audio_cache_xld($Matches)
  1156. {
  1157. if (substr($Matches[2], 0, 2) == 'OK' || substr($Matches[2], 0, 3) == 'YES') {
  1158. $Class = 'good';
  1159. } else {
  1160. $Class = 'bad';
  1161. $this->account('"Disable audio cache" should be yes/ok', 10);
  1162. }
  1163. return '<span class="log5">Disable audio cache' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1164. }
  1165. function c2_pointers($Matches)
  1166. {
  1167. if (strtolower($Matches[2]) == 'yes') {
  1168. $Class = 'bad';
  1169. $this->account('C2 pointers were used', 10);
  1170. } else {
  1171. $Class = 'good';
  1172. }
  1173. return '<span class="log5">Make use of C2 pointers' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1174. }
  1175. function c2_pointers_eac_pre99($Matches)
  1176. {
  1177. if (strtolower($Matches[1]) == 'no ') {
  1178. $Class = 'good';
  1179. } else {
  1180. $Class = 'bad';
  1181. $this->account('C2 pointers were used', 10);
  1182. }
  1183. return '<span>with </span><span class="' . $Class . '">' . $Matches[1] . 'C2</span>';
  1184. }
  1185. function read_offset($Matches)
  1186. {
  1187. if ($this->DriveFound == true) {
  1188. if (in_array($Matches[2], $this->Offsets)) {
  1189. $Class = 'good';
  1190. } else {
  1191. $Class = 'bad';
  1192. $this->account('Incorrect read offset for drive. Correct offsets are: ' . implode(', ', $this->Offsets) . ' (Checked against the following drive(s): ' . implode(', ', $this->Drives) . ')', 5, false, false, false, 5);
  1193. }
  1194. } else {
  1195. if ($Matches[2] == 0) {
  1196. $Class = 'bad';
  1197. $this->account('The drive was not found in the database, so we cannot determine the correct read offset. However, the read offset in this case was 0, which is almost never correct. As such, we are assuming that the offset is incorrect', 5, false, false, false, 5);
  1198. } else {
  1199. $Class = 'badish';
  1200. }
  1201. }
  1202. return '<span class="log5">Read offset correction' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1203. }
  1204. function fill_offset_samples($Matches)
  1205. {
  1206. if ($Matches[2] == 'Yes') {
  1207. $Class = 'good';
  1208. } else {
  1209. $Class = 'bad';
  1210. $this->account('Does not fill up missing offset samples with silence', 5, false, false, false, 5);
  1211. }
  1212. return '<span class="log5">Fill up missing offset samples with silence' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1213. }
  1214. function delete_silent_blocks($Matches)
  1215. {
  1216. if ($Matches[2] == 'Yes') {
  1217. $Class = 'bad';
  1218. $this->account('Deletes leading and trailing silent blocks', 5, false, false, false, 5);
  1219. } else {
  1220. $Class = 'good';
  1221. }
  1222. return '<span class="log5">Delete leading and trailing silent blocks' . $Matches[1] . $Matches[2] .'</span>: <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1223. }
  1224. function null_samples($Matches)
  1225. {
  1226. if ($Matches[2] == 'Yes') {
  1227. $Class = 'good';
  1228. } else {
  1229. $Class = 'bad';
  1230. $this->account('Null samples should be used in CRC calculations', 5);
  1231. }
  1232. return '<span class="log5">Null samples used in CRC calculations' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1233. }
  1234. function gap_handling($Matches)
  1235. {
  1236. if (strpos($Matches[2], 'Not detected') !== false) {
  1237. $Class = 'bad';
  1238. $this->account('Gap handling was not detected', 10);
  1239. } elseif (strpos($Matches[2], 'Appended to next track') !== false) {
  1240. $Class = 'bad';
  1241. $this->account('Gap handling should be appended to previous track', 5);
  1242. } elseif (strpos($Matches[2], 'Appended to previous track') !== false) {
  1243. $Class = 'good';
  1244. } else {
  1245. $Class = 'goodish';
  1246. }
  1247. return '<span class="log5">Gap handling' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1248. }
  1249. function gap_handling_xld($Matches)
  1250. {
  1251. if (strpos(strtolower($Matches[2]), 'not') !== false) { //?
  1252. $Class = 'bad';
  1253. $this->account('Incorrect gap handling', 10, false, false, false, 5);
  1254. } elseif (strpos(strtolower($Matches[2]), 'analyzed') !== false && strpos(strtolower($Matches[2]), 'appended') !== false) {
  1255. $Class = 'good';
  1256. } else {
  1257. $Class = 'badish';
  1258. $this->account('Incomplete gap handling', 10, false, false, false, 3);
  1259. }
  1260. return '<span class="log5">Gap status' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1261. }
  1262. function add_id3_tag($Matches)
  1263. {
  1264. if ($Matches[2] == 'Yes') {
  1265. $Class = 'badish';
  1266. $this->account('ID3 tags should not be added to FLAC files - they are mainly for MP3 files. FLACs should have vorbis comments for tags instead.', 1);
  1267. } else {
  1268. $Class = 'good';
  1269. }
  1270. return '<span class="log5">Add ID3 tag' . $Matches[1] . '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1271. }
  1272. function test_copy($Matches)
  1273. {
  1274. if ($this->RIPPER == "EAC") {
  1275. if ($Matches[1] == $Matches[3]) {
  1276. $Class = 'good';
  1277. } else {
  1278. $Class = 'bad';
  1279. $this->account_track("CRC mismatch: $Matches[1] and $Matches[3]", 30);
  1280. if (!$this->SecureMode) {
  1281. $this->DecreaseScoreTrack += 20;
  1282. $this->BadTrack[] = 'Rip ' . (($this->Combined) ? " (" . $this->CurrLog . ") " : '') . 'was not done in Secure mode, and experienced CRC mismatches (-20 points)';
  1283. $this->SecureMode = true;
  1284. }
  1285. }
  1286. return "<span class=\"log4\">Test CRC <span class=\"$Class\">$Matches[1]</span></span>\n$Matches[2]<span class=\"log4\">Copy CRC <span class=\"$Class\">$Matches[3]</span></span>";
  1287. }
  1288. elseif ($this->RIPPER == "XLD") {
  1289. if ($Matches[2] == $Matches[5]) {
  1290. $Class = 'good';
  1291. } else {
  1292. $Class = 'bad';
  1293. $this->account_track("CRC mismatch: $Matches[2] and $Matches[5]", 30);
  1294. if (!$this->SecureMode) {
  1295. $this->DecreaseScoreTrack += 20;
  1296. $this->BadTrack[] = 'Rip ' . (($this->Combined) ? " (" . $this->CurrLog . ") " : '') . 'was not done with Secure Ripper / in CDParanoia mode, and experienced CRC mismatches (-20 points)';
  1297. $this->SecureMode = true;
  1298. }
  1299. }
  1300. return "<span class=\"log4\">CRC32 hash (test run)$Matches[1] <span class=\"$Class\">$Matches[2]</span></span>\n$Matches[3]<span class=\"log4\">CRC32 hash$Matches[4] <span class=\"$Class\">$Matches[5]</span></span>";
  1301. }
  1302. }
  1303. function xld_all_stat($Matches)
  1304. {
  1305. if (strtolower($Matches[1]) == 'read error' || strtolower($Matches[1]) == 'skipped (treated as error)' || strtolower($Matches[1]) == 'inconsistency in error sectors' || strtolower($Matches[1]) == 'damaged sector count') {
  1306. if ($Matches[3] == 0) {
  1307. $Class = 'good';
  1308. } else {
  1309. $Class = 'bad';
  1310. }
  1311. return '<span class="log4">' . $Matches[1] . $Matches[2] . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1312. }
  1313. if (strtolower($Matches[1]) == 'retry sector count' || strtolower($Matches[1]) == 'jitter error (maybe fixed)' || strtolower($Matches[1]) == 'edge jitter error (maybe fixed)' || strtolower($Matches[1]) == 'atom jitter error (maybe fixed)' || strtolower($Matches[1]) == 'drift error (maybe fixed)' || strtolower($Matches[1]) == 'dropped bytes error (maybe fixed)' || strtolower($Matches[1]) == 'duplicated bytes error (maybe fixed)') {
  1314. if ($Matches[3] == 0) {
  1315. $Class = 'goodish';
  1316. } else {
  1317. $Class = 'badish';
  1318. }
  1319. return '<span class="log4">' . $Matches[1] . $Matches[2] . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1320. }
  1321. }
  1322. function xld_stat($Matches)
  1323. {
  1324. if (strtolower($Matches[1]) == 'read error') {
  1325. if ($Matches[3] == 0) {
  1326. $Class = 'good';
  1327. } else {
  1328. $Class = 'bad';
  1329. $err = ($Matches[3] > 10) ? 10 : $Matches[3]; //max.
  1330. $this->account_track('Read error' . ($Matches[3] == 1 ? '' : 's') . ' detected', $err);
  1331. }
  1332. return '<span class="log4">' . $Matches[1] . $Matches[2] . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1333. }
  1334. if (strtolower($Matches[1]) == 'skipped (treated as error)') {
  1335. if ($Matches[3] == 0) {
  1336. $Class = 'good';
  1337. } else {
  1338. $Class = 'bad';
  1339. $err = ($Matches[3] > 10) ? 10 : $Matches[3]; //max.
  1340. $this->account_track('Skipped error' . ($Matches[3] == 1 ? '' : 's') . ' detected', $err);
  1341. }
  1342. return '<span class="log4">' . $Matches[1] . $Matches[2] . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1343. }
  1344. if (strtolower($Matches[1]) == 'inconsistency in error sectors') {
  1345. if ($Matches[3] == 0) {
  1346. $Class = 'good';
  1347. } else {
  1348. $Class = 'bad';
  1349. $err = ($Matches[3] > 10) ? 10 : $Matches[3]; //max.
  1350. $this->account_track('Inconsistenc' . (($Matches[3] == 1) ? 'y' : 'ies') . ' in error sectors detected', $err);
  1351. }
  1352. return '<span class="log4">' . $Matches[1] . $Matches[2] . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1353. }
  1354. if (strtolower($Matches[1]) == 'damaged sector count') { //xld secure ripper
  1355. if ($Matches[3] == 0) {
  1356. $Class = 'good';
  1357. } else {
  1358. $Class = 'bad';
  1359. $err = ($Matches[3] > 10) ? 10 : $Matches[3]; //max.
  1360. $this->account_track('Damaged sector count of ' . ($Matches[3]), $err);
  1361. }
  1362. return '<span class="log4">' . $Matches[1] . $Matches[2] . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1363. }
  1364. if (strtolower($Matches[1]) == 'retry sector count' || strtolower($Matches[1]) == 'jitter error (maybe fixed)' || strtolower($Matches[1]) == 'edge jitter error (maybe fixed)' || strtolower($Matches[1]) == 'atom jitter error (maybe fixed)' || strtolower($Matches[1]) == 'drift error (maybe fixed)' || strtolower($Matches[1]) == 'dropped bytes error (maybe fixed)' || strtolower($Matches[1]) == 'duplicated bytes error (maybe fixed)') {
  1365. if ($Matches[3] == 0) {
  1366. $Class = 'goodish';
  1367. } else {
  1368. $Class = 'badish';
  1369. }
  1370. return '<span class="log4">' . $Matches[1] . $Matches[2] . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1371. }
  1372. }
  1373. function toc($Matches)
  1374. {
  1375. return "$Matches[1]<span class=\"log4\">$Matches[2]</span>$Matches[3]<strong>|</strong>$Matches[4]<span class=\"log1\">$Matches[5]</span>$Matches[7]<strong>|</strong>$Matches[8]<span class=\"log1\">$Matches[9]</span>$Matches[11]<strong>|</strong>$Matches[12]<span class=\"log1\">$Matches[13]</span>$Matches[14]<strong>|</strong>$Matches[15]<span class=\"log1\">$Matches[16]</span>$Matches[17]" . "\n";
  1376. }
  1377. function check_tracks()
  1378. {
  1379. if (!count($this->Tracks)) { //no tracks
  1380. unset($this->Details);
  1381. if ($this->Combined) {
  1382. $this->Details[] = "Combined Log (" . $this->Combined . ")";
  1383. $this->Details[] = "Invalid log (" . $this->CurrLog . "), no tracks!";
  1384. } else {
  1385. $this->Details[] = "Invalid log, no tracks!";
  1386. }
  1387. $this->Score = 0;
  1388. return $this->return_parse();
  1389. }
  1390. }
  1391. function account($Msg, $Decrease = false, $Score = false, $InclCombined = false, $Notice = false, $DecreaseBoost = false)
  1392. {
  1393. $DecreaseScore = $SetScore = false;
  1394. $Append2 = '';
  1395. $Append1 = ($InclCombined) ? (($this->Combined) ? " (" . $this->CurrLog . ")" : '') : '';
  1396. $Prepend = ($Notice) ? '[Notice] ' : '';
  1397. if ($Decrease) {
  1398. $DecreaseScore = true;
  1399. $Append2 = ($Decrease > 0) ? ' (-' . $Decrease . ' point' . ($Decrease == 1 ? '' : 's') . ')' : '';
  1400. } else if ($Score || $Score === 0) {
  1401. $SetScore = true;
  1402. $Decrease = 100 - $Score;
  1403. $Append2 = ($Decrease > 0) ? ' (-' . $Decrease . ' point' . ($Decrease == 1 ? '' : 's') . ')' : '';
  1404. }
  1405. if (!in_array($Prepend . $Msg . $Append1 . $Append2, $this->Details)) {
  1406. $this->Details[] = $Prepend . $Msg . $Append1 . $Append2;
  1407. if ($DecreaseScore) {
  1408. $this->Score -= $Decrease;
  1409. }
  1410. if ($SetScore) {
  1411. $this->Score = $Score;
  1412. }
  1413. if ($DecreaseBoost) {
  1414. $this->DecreaseBoost += $DecreaseBoost;
  1415. }
  1416. }
  1417. }
  1418. function account_track($Msg, $Decrease = false)
  1419. {
  1420. $tn = (intval($this->TrackNumber) < 10) ? '0' . intval($this->TrackNumber) : $this->TrackNumber;
  1421. $Append = '';
  1422. if ($Decrease) {
  1423. $this->DecreaseScoreTrack += $Decrease;
  1424. $Append = ' (-' . $Decrease . ' point' . ($Decrease == 1 ? '' : 's') . ')';
  1425. }
  1426. $Prepend = 'Track ' . $tn . (($this->Combined) ? " (" . $this->CurrLog . ")" : '') . ': ';
  1427. $this->BadTrack[] = $Prepend . $Msg . $Append;
  1428. }
  1429. function return_parse() {
  1430. return array(
  1431. $this->Score,
  1432. $this->Details,
  1433. $this->Checksum,
  1434. $this->Log
  1435. );
  1436. }
  1437. function get_ripper() {
  1438. return $this->RIPPER;
  1439. }
  1440. function get_version() {
  1441. return $this->Version;
  1442. }
  1443. public static function get_accept_values() {
  1444. return ".txt,.TXT,.log,.LOG";
  1445. }
  1446. }