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.

2008 rivejä
81 KiB

  1. <?php
  2. namespace OrpheusNET\Logchecker;
  3. use OrpheusNET\Logchecker\Check\Ripper;
  4. use OrpheusNET\Logchecker\Parser\EAC\Translator;
  5. use Symfony\Component\Yaml\Yaml;
  6. use Symfony\Component\Yaml\Exception\ParseException;
  7. /*******************************************************************
  8. * Automated EAC/XLD log checker *
  9. ********************************************************************/
  10. class Logchecker
  11. {
  12. private $log = '';
  13. private $logPath = null;
  14. private $logs = array();
  15. private $language = 'en';
  16. private $Tracks = array();
  17. private $checksumStatus = Check\Checksum::CHECKSUM_OK;
  18. private $Score = 100;
  19. private $Details = array();
  20. private $Offsets = array();
  21. private $DriveFound = false;
  22. private $AllDrives = [];
  23. private $Drives = [];
  24. private $SecureMode = true;
  25. private $NonSecureMode = null;
  26. private $BadTrack = array();
  27. private $DecreaseScoreTrack = 0;
  28. private $ripper = null;
  29. private $ripperVersion = null;
  30. private $TrackNumber = null;
  31. private $ARTracks = array();
  32. private $Combined = null;
  33. private $CurrLog = null;
  34. private $Range = null;
  35. private $ARSummary = null;
  36. private $XLDSecureRipper = false;
  37. private $FakeDrives = [
  38. 'Generic DVD-ROM SCSI CdRom Device'
  39. ];
  40. private $ValidateChecksum = true;
  41. public function __construct()
  42. {
  43. $this->AllDrives = json_decode(
  44. file_get_contents(implode(DIRECTORY_SEPARATOR, [__DIR__, 'resources', 'drives.json']))
  45. );
  46. }
  47. public function getLog(): string
  48. {
  49. return $this->log;
  50. }
  51. /**
  52. * @param string $LogPath path to log file on local filesystem
  53. */
  54. public function newFile(string $LogPath): void
  55. {
  56. $this->reset();
  57. $this->logPath = $LogPath;
  58. $this->log = file_get_contents($this->logPath);
  59. }
  60. private function reset(): void
  61. {
  62. $this->logPath = null;
  63. $this->logs = array();
  64. $this->Tracks = array();
  65. $this->checksumStatus = Check\Checksum::CHECKSUM_OK;
  66. $this->Score = 100;
  67. $this->Details = array();
  68. $this->Offsets = array();
  69. $this->DriveFound = false;
  70. $this->Drives = array();
  71. $this->SecureMode = true;
  72. $this->NonSecureMode = null;
  73. $this->BadTrack = array();
  74. $this->DecreaseScoreTrack = 0;
  75. $this->ripper = null;
  76. $this->ripperVersion = null;
  77. $this->TrackNumber = null;
  78. $this->ARTracks = array();
  79. $this->Combined = null;
  80. $this->CurrLog = null;
  81. $this->Range = null;
  82. $this->ARSummary = null;
  83. $this->XLDSecureRipper = false;
  84. }
  85. public function validateChecksum(bool $Bool): void
  86. {
  87. $this->ValidateChecksum = $Bool;
  88. }
  89. public function parse(): void
  90. {
  91. try {
  92. $this->log = Util::decodeEncoding($this->log, $this->logPath);
  93. } catch (\Exception $exc) {
  94. $this->Score = 0;
  95. $this->account('Could not detect log encoding, log is corrupt.');
  96. }
  97. $this->ripper = Ripper::getRipper($this->log);
  98. if ($this->ripper === Ripper::WHIPPER) {
  99. $this->whipperParse();
  100. } else {
  101. $this->legacyParse();
  102. }
  103. }
  104. private function whipperParse(): void
  105. {
  106. if (preg_match('/whipper ([0-9]+\.[0-9]+\.[0-9])/', $this->log, $matches)) {
  107. if (version_compare('0.7.3', $matches[1]) === 1) {
  108. $this->account('Logs must be produced by whipper 0.7.3+.', 100);
  109. return;
  110. }
  111. }
  112. // Whipper 0.7.x has an issue where it can produce invalid YAML
  113. // as it hand writes out the values without dealing properly
  114. // with the escaping the output, so we fix that here
  115. $log = preg_replace_callback('/ (Release|Album): (.+)/', function ($match) {
  116. return " {$match[1]}: " . Yaml::dump($match[2]);
  117. }, $this->log);
  118. // symfony/yaml will attempt to parse CRCs that start with 0 as octals
  119. $log = preg_replace_callback('/CRC: ([A-Z0-9]+)/', function ($match) {
  120. return "CRC: \"{$match[1]}\"";
  121. }, $log);
  122. try {
  123. $Yaml = Yaml::parse($log);
  124. } catch (ParseException $exception) {
  125. $this->account('Could not parse whipper log.', 100);
  126. return;
  127. }
  128. $this->ripperVersion = explode(" ", $Yaml['Log created by'])[1];
  129. // Releases before this used octal numbers for tracks in the log which
  130. // gets messed up in parsing and we lose track data (e.g. tracks 08 and
  131. // 09 get merged into one entry).
  132. if (empty($this->ripperVersion) || version_compare('0.7.3', $this->ripperVersion) === 1) {
  133. $this->account('Logs must be produced by whipper 0.7.3+', 100);
  134. return;
  135. }
  136. $Yaml['Log created by'] = preg_replace(
  137. '/^(whipper) ([^\s]+)/',
  138. "<span class='good'>$1</span> <span class='log1'>$2</span>",
  139. $Yaml['Log created by']
  140. );
  141. if (empty($Yaml['SHA-256 hash'])) {
  142. $this->checksumStatus = Check\Checksum::CHECKSUM_MISSING;
  143. } else {
  144. $this->checksumStatus = Check\Checksum::validate($this->logPath, $this->ripper);
  145. $Class = $this->checksumStatus === Check\Checksum::CHECKSUM_OK ? 'good' : 'bad';
  146. $Yaml['SHA-256 hash'] = "<span class='{$Class}'>{$Yaml['SHA-256 hash']}</span>";
  147. }
  148. $Key = 'Ripping phase information';
  149. $Drive = $Yaml[$Key]['Drive'];
  150. $Offset = $Yaml[$Key]['Read offset correction'];
  151. if (in_array(trim($Drive), $this->FakeDrives)) {
  152. $this->account('Virtual drive used: ' . $Drive, 20, false, false, false);
  153. $Yaml[$Key]['Drive'] = "<span class='bad'>{$Drive}</span>";
  154. } else {
  155. $this->getDrives($Drive);
  156. $DriveClass = 'badish';
  157. if (count($this->Drives) > 0) {
  158. $DriveClass = 'good';
  159. if (in_array((string) $Offset, $this->Offsets)) {
  160. $OffsetClass = 'good';
  161. } else {
  162. $OffsetClass = 'bad';
  163. $this->account(
  164. 'Incorrect read offset for drive. Correct offsets are: ' .
  165. implode(', ', $this->Offsets) . ' (Checked against the following drive(s): ' .
  166. implode(', ', $this->Drives) . ')',
  167. 5,
  168. false,
  169. false,
  170. false
  171. );
  172. }
  173. } else {
  174. $Drive .= ' (not found in database)';
  175. $OffsetClass = 'badish';
  176. if ($Offset === '0') {
  177. $OffsetClass = 'bad';
  178. $this->account(
  179. 'The drive was not found in the database, so we cannot determine the correct read ' .
  180. 'offset. However, the read offset in this case was 0, which is almost never correct. ' .
  181. 'As such, we are assuming that the offset is incorrect',
  182. 5,
  183. false,
  184. false,
  185. false
  186. );
  187. }
  188. }
  189. $Yaml[$Key]['Drive'] = "<span class='{$DriveClass}'>{$Drive}</span>";
  190. $Offset = ($Offset > 0) ? '+' . (string) $Offset : (string) $Offset;
  191. $Yaml[$Key]['Read offset correction'] = "<span class='{$OffsetClass}'>{$Offset}</span>";
  192. }
  193. $DefeatCache = $Yaml[$Key]['Defeat audio cache'];
  194. if (is_string($DefeatCache)) {
  195. $Value = (strtolower($DefeatCache) === 'yes') ? 'Yes' : 'No';
  196. $Class = (strtolower($DefeatCache) === 'yes') ? 'good' : 'bad';
  197. } else {
  198. $Value = ($DefeatCache === true) ? 'true' : 'false';
  199. $Class = ($DefeatCache === true) ? 'good' : 'bad';
  200. }
  201. if ($Class === 'bad') {
  202. $this->account('"Defeat audio cache" should be Yes/true', 10);
  203. }
  204. $Yaml[$Key]['Defeat audio cache'] = "<span class='{$Class}'>{$Value}</span>";
  205. $Yaml[$Key]['Overread into lead-out'] = "<span class='log4'>{$Yaml[$Key]['Overread into lead-out']}</span>";
  206. // CD Metadata
  207. $Key = 'CD metadata';
  208. $ReleaseKey = isset($Yaml[$Key]['Release']) ? 'Release' : 'Album';
  209. if (is_string($Yaml[$Key][$ReleaseKey])) {
  210. $Yaml[$Key][$ReleaseKey] = "<span class='log4'>{$Yaml[$Key][$ReleaseKey]}</span>";
  211. } else {
  212. $Yaml[$Key][$ReleaseKey]['Artist'] = "<span class='log4'>{$Yaml[$Key][$ReleaseKey]['Artist']}</span>";
  213. $Yaml[$Key][$ReleaseKey]['Title'] = "<span class='log4'>{$Yaml[$Key][$ReleaseKey]['Title']}</span>";
  214. }
  215. // TOC
  216. foreach ($Yaml['TOC'] as &$Track) {
  217. foreach (['Start', 'Length', 'Start sector', 'End sector'] as $Key) {
  218. $Track[$Key] = "<span class='log1'>{$Track[$Key]}</span>";
  219. }
  220. }
  221. // Tracks
  222. foreach ($Yaml['Tracks'] as &$Track) {
  223. $Track['Peak level'] = sprintf('%.6f', $Track['Peak level']);
  224. $Class = 'good';
  225. if ($Track['Test CRC'] !== $Track['Copy CRC']) {
  226. $Class = 'bad';
  227. $this->account("CRC mismatch: {$Track['Test CRC']} and {$Track['Copy CRC']}", 30);
  228. }
  229. $Track['Test CRC'] = "<span class='{$Class}'>{$Track['Test CRC']}</span>";
  230. $Track['Copy CRC'] = "<span class='{$Class}'>{$Track['Copy CRC']}</span>";
  231. $Class = ($Track['Status'] === 'Copy OK') ? 'good' : 'bad';
  232. $Track['Status'] = "<span class='{$Class}'>{$Track['Status']}</span>";
  233. foreach (['Filename', 'Pre-gap length', 'Peak level', 'Extraction speed', 'Extraction quality'] as $Key) {
  234. if (!isset($Track[$Key])) {
  235. continue;
  236. }
  237. $Track[$Key] = "<span class='log3'>{$Track[$Key]}</span>";
  238. }
  239. foreach (['v1', 'v2'] as $Version) {
  240. $Key = 'AccurateRip ' . $Version;
  241. if (isset($Track[$Key])) {
  242. $Class = $Track[$Key]['Result'] === 'Found, exact match' ? 'good' : 'badish';
  243. $Track[$Key]['Result'] = "<span class='{$Class}'>{$Track[$Key]['Result']}</span>";
  244. if (isset($Track[$Key]['Local CRC']) && isset($Track[$Key]['Remote CRC'])) {
  245. $Class = ($Track[$Key]['Local CRC'] === $Track[$Key]['Remote CRC']) ? 'goodish' : 'badish';
  246. $Track[$Key]['Local CRC'] = "<span class='{$Class}'>{$Track[$Key]['Local CRC']}</span>";
  247. $Track[$Key]['Remote CRC'] = "<span class='{$Class}'>{$Track[$Key]['Remote CRC']}</span>";
  248. }
  249. }
  250. }
  251. }
  252. // Conclusive status report
  253. $Key = 'Conclusive status report';
  254. $Class = $Yaml[$Key]['AccurateRip summary'] === 'All tracks accurately ripped' ? 'good' : 'badish';
  255. $Yaml[$Key]['AccurateRip summary'] = "<span class='{$Class}'>{$Yaml[$Key]['AccurateRip summary']}</span>";
  256. $Class = $Yaml[$Key]['Health status'] === 'No errors occurred' ? 'good' : 'bad';
  257. $Yaml[$Key]['Health status'] = "<span class='{$Class}'>{$Yaml[$Key]['Health status']}</span>";
  258. $CreationDate = gmdate("Y-m-d\TH:i:s\Z", $Yaml['Log creation date']);
  259. $this->log = "Log created by: {$Yaml['Log created by']}\nLog creation date: {$CreationDate}\n\n";
  260. $this->log .= "Ripping phase information:\n";
  261. foreach ($Yaml['Ripping phase information'] as $Key => $Value) {
  262. if (is_bool($Value)) {
  263. $Value = ($Value) ? 'true' : 'false';
  264. }
  265. $this->log .= " {$Key}: {$Value}\n";
  266. }
  267. $this->log .= "\n";
  268. $this->log .= "CD metadata:\n";
  269. foreach ($Yaml['CD metadata'] as $Key => $Value) {
  270. if (is_bool($Value)) {
  271. $Value = ($Value) ? 'true' : 'false';
  272. }
  273. $this->log .= " {$Key}: {$Value}\n";
  274. }
  275. $this->log .= "\n";
  276. $this->log .= "TOC:\n";
  277. foreach ($Yaml['TOC'] as $Key => $Track) {
  278. $this->log .= " {$Key}:\n";
  279. foreach ($Track as $KKey => $Value) {
  280. $this->log .= " {$KKey}: {$Value}\n";
  281. }
  282. $this->log .= "\n";
  283. }
  284. $this->log .= "Tracks:\n";
  285. foreach ($Yaml['Tracks'] as $Key => $Track) {
  286. $this->log .= " {$Key}:\n";
  287. foreach ($Track as $KKey => $Value) {
  288. if (is_array($Value)) {
  289. $this->log .= " {$KKey}:\n";
  290. foreach ($Value as $KKKey => $VValue) {
  291. $this->log .= " {$KKKey}: {$VValue}\n";
  292. }
  293. } else {
  294. if (is_bool($Value)) {
  295. $Value = ($Value) ? 'Yes' : 'No';
  296. }
  297. $this->log .= " {$KKey}: {$Value}\n";
  298. }
  299. }
  300. $this->log .= "\n";
  301. }
  302. $this->log .= "Conclusive status report:\n";
  303. foreach ($Yaml['Conclusive status report'] as $Key => $Value) {
  304. $this->log .= " {$Key}: {$Value}\n";
  305. }
  306. $this->log .= "\n";
  307. if (isset($Yaml['SHA-256 hash'])) {
  308. $this->log .= "SHA-256 hash: {$Yaml['SHA-256 hash']}\n";
  309. }
  310. }
  311. private function legacyParse()
  312. {
  313. if ($this->ripper === Ripper::EAC) {
  314. $translator = new Translator();
  315. $lang = $translator->getLanguage($this->log);
  316. if ($lang['code'] !== 'en') {
  317. $this->language = $lang['code'];
  318. $this->account(
  319. "Translated log from {$lang['name']} ({$lang['name_english']}) to English.",
  320. false,
  321. false,
  322. false,
  323. true
  324. );
  325. $this->log = $translator->translate($this->log, $lang['code']);
  326. }
  327. }
  328. $this->log = str_replace(array("\r\n", "\r"), array("\n", ""), $this->log);
  329. // Split the log apart
  330. if (preg_match("/[\=]+\s+Log checksum/i", $this->log)) { // eac checksum
  331. $this->logs = preg_split("/(\n\=+\s+Log checksum.*)/i", $this->log, -1, PREG_SPLIT_DELIM_CAPTURE);
  332. } elseif (
  333. preg_match(
  334. "/[\-]+BEGIN XLD SIGNATURE[\S\n\-]+END XLD SIGNATURE[\-]+/i",
  335. $this->log
  336. )
  337. ) { // xld checksum (plugin)
  338. $this->logs = preg_split(
  339. "/(\n[\-]+BEGIN XLD SIGNATURE[\S\n\-]+END XLD SIGNATURE[\-]+)/i",
  340. $this->log,
  341. -1,
  342. PREG_SPLIT_DELIM_CAPTURE
  343. );
  344. } else { //no checksum
  345. $this->checksumStatus = Check\Checksum::CHECKSUM_MISSING;
  346. $this->logs = preg_split("/(\nEnd of status report)/i", $this->log, -1, PREG_SPLIT_DELIM_CAPTURE);
  347. foreach ($this->logs as $Key => $Value) {
  348. if (preg_match("/---- CUETools DB Plugin V.+/i", $Value)) {
  349. unset($this->logs[$Key]);
  350. }
  351. }
  352. }
  353. foreach ($this->logs as $Key => $Log) {
  354. $Log = trim($Log);
  355. if ($Log === "" || preg_match('/^\-+$/i', $Log)) {
  356. unset($this->logs[$Key]);
  357. } elseif (
  358. $this->checksumStatus !== Check\Checksum::CHECKSUM_OK
  359. && preg_match("/End of status report/i", $Log)
  360. ) {
  361. //strip empty
  362. //append stat msgs
  363. $this->logs[$Key - 1] .= $Log;
  364. unset($this->logs[$Key]);
  365. } elseif (
  366. $this->checksumStatus === Check\Checksum::CHECKSUM_OK
  367. && preg_match("/[\=]+\s+Log checksum/i", $Log)
  368. ) {
  369. $this->logs[$Key - 1] .= $Log;
  370. unset($this->logs[$Key]);
  371. } elseif (
  372. $this->checksumStatus === Check\Checksum::CHECKSUM_OK
  373. && preg_match("/[\-]+BEGIN XLD SIGNATURE/i", $Log)
  374. ) {
  375. $this->logs[$Key - 1] .= $Log;
  376. unset($this->logs[$Key]);
  377. }
  378. }
  379. $this->logs = array_values($this->logs); //rebuild index
  380. if (count($this->logs) > 1) {
  381. $this->Combined = count($this->logs);
  382. } //is_combined
  383. foreach ($this->logs as $LogArrayKey => $Log) {
  384. $this->CurrLog = $LogArrayKey + 1;
  385. if (preg_match('/Exact Audio Copy (.+) from/i', $Log, $Matches)) { //eac v1 & checksum
  386. if ($Matches[1]) {
  387. $this->ripperVersion = ltrim($Matches[1], 'V');
  388. $versionCheck = explode(" ", $this->ripperVersion)[0];
  389. if (version_compare($versionCheck, "1.0") < 0) {
  390. $this->checksumStatus = Check\Checksum::CHECKSUM_MISSING;
  391. if ($this->ripperVersion <= 0.95) {
  392. # EAC 0.95 and before was missing a handful of stuff for full log validation
  393. # that 0.99 included (-30 points)
  394. $this->account("EAC version older than 0.99", 30);
  395. }
  396. } elseif (!preg_match('/(\=+\s+Log checksum.*)/i', $Log)) {
  397. // Above version 1 and no checksum
  398. $this->checksumStatus = Check\Checksum::CHECKSUM_MISSING;
  399. }
  400. } else {
  401. $this->checksumStatus = Check\Checksum::CHECKSUM_MISSING;
  402. $this->account("EAC version older than 0.99", 30);
  403. }
  404. } elseif (preg_match('/EAC extraction logfile from/i', $Log)) {
  405. $this->checksumStatus = Check\Checksum::CHECKSUM_MISSING;
  406. $this->account("EAC version older than 0.99", 30);
  407. }
  408. if (preg_match('/X Lossless Decoder version (\d+) \((.+)\)/i', $Log, $Matches)) { //xld version & checksum
  409. $this->ripperVersion = $Matches[1];
  410. if (
  411. version_compare($this->ripperVersion, "20121222") >= 0
  412. && !preg_match('/([\-]+BEGIN XLD SIGNATURE[\S\n\-]+END XLD SIGNATURE[\-]+)/i', $Log)
  413. ) {
  414. $this->checksumStatus = Check\Checksum::CHECKSUM_MISSING;
  415. //$this->account('No checksum with XLD 20121222 or newer', 15);
  416. }
  417. }
  418. $Log = preg_replace(
  419. '/Exact Audio Copy (.+) from (.+)/i',
  420. 'Exact Audio Copy <span class="log1">$1</span> from <span class="log1">$2</span>',
  421. $Log,
  422. 1,
  423. $Count
  424. );
  425. $Log = preg_replace(
  426. "/EAC extraction logfile from (.+)\n+(.+)/i",
  427. "<span class='good'>EAC extraction logfile from <span class='log5'>$1</span></span>\n\n" .
  428. '<span class="log4">$2</span>',
  429. $Log,
  430. 1,
  431. $EAC
  432. );
  433. $Log = preg_replace(
  434. "/X Lossless Decoder version (.+) \((.+)\)/i",
  435. "X Lossless Decoder version <span class=\"log1\">$1</span> (<span class=\"log1\">$2</span>)",
  436. $Log,
  437. 1,
  438. $Count
  439. );
  440. $Log = preg_replace(
  441. "/XLD extraction logfile from (.+)\n+(.+)/i",
  442. "<span class='good'>XLD extraction logfile from <span class='log5'>$1</span></span>\n\n" .
  443. '<span class="log4">$2</span>',
  444. $Log,
  445. 1,
  446. $XLD
  447. );
  448. if (!$EAC && !$XLD) {
  449. if ($this->Combined) {
  450. unset($this->Details);
  451. $this->Details[] = "Combined Log (" . $this->Combined . ")";
  452. $this->Details[] = "Unrecognized log file (" . $this->CurrLog . ")! " .
  453. "Feel free to report for manual review.";
  454. } else {
  455. $this->Details[] = "Unrecognized log file! Feel free to report for manual review.";
  456. }
  457. $this->Score = 0;
  458. return;
  459. }
  460. if (
  461. $this->ValidateChecksum
  462. && $this->checksumStatus === Check\Checksum::CHECKSUM_OK
  463. && !empty($this->logPath)
  464. ) {
  465. if (Check\Checksum::logcheckerExists($this->ripper)) {
  466. $this->checksumStatus = Check\Checksum::validate($this->logPath, $this->ripper);
  467. } else {
  468. $this->account(
  469. "Could not find {$this->ripper} logchecker, checksum not validated.",
  470. false,
  471. false,
  472. false,
  473. true
  474. );
  475. }
  476. }
  477. $Class = $this->checksumStatus === Check\Checksum::CHECKSUM_OK ? 'good' : 'bad';
  478. $Log = preg_replace('/(\=+\s+Log checksum.*)/i', "<span class='{$Class}'>$1</span>", $Log, 1, $Count);
  479. $Log = preg_replace(
  480. '/([\-]+BEGIN XLD SIGNATURE[\S\n\-]+END XLD SIGNATURE[\-]+)/i',
  481. "<span class='{$Class}'>$1</span>",
  482. $Log,
  483. 1,
  484. $Count
  485. );
  486. $Log = preg_replace_callback("/Used drive( *): (.+)/i", array(
  487. $this,
  488. 'drive'
  489. ), $Log, 1, $Count);
  490. if (!$Count) {
  491. $this->account('Could not verify used drive', 1);
  492. }
  493. $Log = preg_replace_callback("/Media type( *): (.+)/i", array(
  494. $this,
  495. 'mediaTypeXld'
  496. ), $Log, 1, $Count);
  497. if ($XLD && $this->ripperVersion && $this->ripperVersion >= 20130127 && !$Count) {
  498. $this->account('Could not verify media type', 1);
  499. }
  500. $Log = preg_replace_callback('/Read mode( *): ([a-z]+)(.*)?/i', array(
  501. $this,
  502. 'readMode'
  503. ), $Log, 1, $Count);
  504. if (!$Count && $EAC) {
  505. $this->account('Could not verify read mode', 1);
  506. }
  507. $Log = preg_replace_callback('/Ripper mode( *): (.*)/i', array(
  508. $this,
  509. 'ripperModeXld'
  510. ), $Log, 1, $XLDRipperMode);
  511. $Log = preg_replace_callback('/Use cdparanoia mode( *): (.*)/i', array(
  512. $this,
  513. 'cdparanoiaModeXld'
  514. ), $Log, 1, $XLDCDParanoiaMode);
  515. if (!$XLDRipperMode && !$XLDCDParanoiaMode && $XLD) {
  516. $this->account('Could not verify read mode', 1);
  517. }
  518. $Log = preg_replace_callback('/Max retry count( *): (\d+)/i', array(
  519. $this,
  520. 'maxRetryCount'
  521. ), $Log, 1, $Count);
  522. if (!$Count && $XLD) {
  523. $this->account('Could not verify max retry count');
  524. }
  525. $Log = preg_replace_callback('/Utilize accurate stream( *): (Yes|No)/i', array(
  526. $this,
  527. 'accurateStream'
  528. ), $Log, 1, $EAC_ac_stream);
  529. $Log = preg_replace_callback('/, (|NO )accurate stream/i', array(
  530. $this,
  531. 'accurateStreamEacPre9'
  532. ), $Log, 1, $EAC_ac_stream_pre99);
  533. if (!$EAC_ac_stream && !$EAC_ac_stream_pre99 && !$this->NonSecureMode && $EAC) {
  534. $this->account('Could not verify accurate stream', 20);
  535. }
  536. $Log = preg_replace_callback('/Defeat audio cache( *): (Yes|No)/i', array(
  537. $this,
  538. 'defeatAudioCache'
  539. ), $Log, 1, $EAC_defeat_cache);
  540. $Log = preg_replace_callback('/ (|NO )disable cache/i', array(
  541. $this,
  542. 'defeatAudioCacheEacPre99'
  543. ), $Log, 1, $EAC_defeat_cache_pre99);
  544. if (!$EAC_defeat_cache && !$EAC_defeat_cache_pre99 && !$this->NonSecureMode && $EAC) {
  545. $this->account('Could not verify defeat audio cache', 1);
  546. }
  547. $Log = preg_replace_callback('/Disable audio cache( *): (.*)/i', array(
  548. $this,
  549. 'defeatAudioCacheXld'
  550. ), $Log, 1, $Count);
  551. if (!$Count && $XLD) {
  552. $this->account('Could not verify defeat audio cache', 1);
  553. }
  554. $Log = preg_replace_callback('/Make use of C2 pointers( *): (Yes|No)/i', array(
  555. $this,
  556. 'c2Pointers'
  557. ), $Log, 1, $C2);
  558. $Log = preg_replace_callback('/with (|NO )C2/i', array(
  559. $this,
  560. 'c2PointersEacPre99'
  561. ), $Log, 1, $C2_EACpre99);
  562. if (!$C2 && !$C2_EACpre99 && !$this->NonSecureMode) {
  563. $this->account('Could not verify C2 pointers', 1);
  564. }
  565. $Log = preg_replace_callback('/Read offset correction( *): ([+-]?[0-9]+)/i', array(
  566. $this,
  567. 'readOffset'
  568. ), $Log, 1, $Count);
  569. if (!$Count) {
  570. $this->account('Could not verify read offset', 1);
  571. }
  572. $Log = preg_replace(
  573. "/(Combined read\/write offset correction\s*:\s+\d+)/i",
  574. "<span class=\"bad\">$1</span>",
  575. $Log,
  576. 1,
  577. $Count
  578. );
  579. if ($Count) {
  580. $this->account('Combined read/write offset cannot be verified', 4, false, false, false);
  581. }
  582. //xld alternate offset table
  583. $Log = preg_replace(
  584. "/(List of \w+ offset correction values) *(\n+)(( *.*confidence .*\) ?\n)+)/i",
  585. "<span class=\"log5\">$1</span>$2<span class=\"log4\">$3</span>\n",
  586. $Log,
  587. 1,
  588. $Count
  589. );
  590. $Log = preg_replace(
  591. "/(List of \w+ offset correction values) *\n( *\# +\| +Absolute +\| +Relative +\| +Confidence) *\n" .
  592. "( *\-+) *\n(( *\d+ +\| +\-?\+?\d+ +\| +\-?\+?\d+ +\| +\d+ *\n)+)/i",
  593. "<span class=\"log5\">$1</span>\n<span class=\"log4\">$2\n$3\n$4\n</span>",
  594. $Log,
  595. 1,
  596. $Count
  597. );
  598. $Log = preg_replace(
  599. '/Overread into Lead-In and Lead-Out( +): (Yes|No)/i',
  600. '<span class="log5">Overread into Lead-In and Lead-Out$1</span>: <span class="log4">$2</span>',
  601. $Log,
  602. 1,
  603. $Count
  604. );
  605. $Log = preg_replace_callback('/Fill up missing offset samples with silence( +): (Yes|No)/i', array(
  606. $this,
  607. 'fillOffsetSamples'
  608. ), $Log, 1, $Count);
  609. if (!$Count && $EAC) {
  610. $this->account('Could not verify missing offset samples', 1);
  611. }
  612. $Log = preg_replace_callback('/Delete leading and trailing silent blocks([ \w]*)( +): (Yes|No)/i', array(
  613. $this,
  614. 'deleteSilentBlocks'
  615. ), $Log, 1, $Count);
  616. if (!$Count && $EAC) {
  617. $this->account('Could not verify silent blocks', 1);
  618. }
  619. $Log = preg_replace_callback('/Null samples used in CRC calculations( +): (Yes|No)/i', array(
  620. $this,
  621. 'nullSamples'
  622. ), $Log, 1, $Count);
  623. if (!$Count && $EAC) {
  624. $this->account('Could not verify null samples');
  625. }
  626. $Log = preg_replace(
  627. '/Used interface( +): ([^\n]+)/i',
  628. '<span class="log5">Used interface$1</span>: <span class="log4">$2</span>',
  629. $Log,
  630. 1,
  631. $Count
  632. );
  633. $Log = preg_replace_callback('/Gap handling( +): ([^\n]+)/i', array(
  634. $this,
  635. 'gapHandling'
  636. ), $Log, 1, $Count);
  637. if (!$Count && $EAC) {
  638. $this->account('Could not verify gap handling', 10);
  639. }
  640. $Log = preg_replace_callback('/Gap status( +): (.*)/i', array(
  641. $this,
  642. 'gapHandlingXld'
  643. ), $Log, 1, $Count);
  644. if (!$Count && $XLD) {
  645. $this->account('Could not verify gap status', 10);
  646. }
  647. $Log = preg_replace(
  648. '/Used output format( *): ([^\n]+)/i',
  649. '<span class="log5">Used output format$1</span>: <span class="log4">$2</span>',
  650. $Log,
  651. 1,
  652. $Count
  653. );
  654. $Log = preg_replace(
  655. '/Sample format( +): ([^\n]+)/i',
  656. '<span class="log5">Sample format$1</span>: <span class="log4">$2</span>',
  657. $Log,
  658. 1,
  659. $Count
  660. );
  661. $Log = preg_replace(
  662. '/Selected bitrate( +): ([^\n]+)/i',
  663. '<span class="log5">Selected bitrate$1</span>: <span class="log4">$2</span>',
  664. $Log,
  665. 1,
  666. $Count
  667. );
  668. $Log = preg_replace(
  669. '/( +)(\d+ kBit\/s)/i',
  670. '$1<span class="log4">$2</span>',
  671. $Log,
  672. 1,
  673. $Count
  674. );
  675. $Log = preg_replace(
  676. '/Quality( +): ([^\n]+)/i',
  677. '<span class="log5">Quality$1</span>: <span class="log4">$2</span>',
  678. $Log,
  679. 1,
  680. $Count
  681. );
  682. $Log = preg_replace_callback('/Add ID3 tag( +): (Yes|No)/i', array(
  683. $this,
  684. 'addId3Tag'
  685. ), $Log, 1, $Count);
  686. if (!$Count && $EAC) {
  687. $this->account('Could not verify id3 tag setting', 1);
  688. }
  689. $Log = preg_replace(
  690. "/(Use compression offset\s+:\s+\d+)/i",
  691. "<span class=\"bad\">$1</span>",
  692. $Log,
  693. 1,
  694. $Count
  695. );
  696. if ($Count) {
  697. $this->account('Ripped with compression offset', false, 0);
  698. }
  699. $Log = preg_replace(
  700. '/Command line compressor( +): ([^\n]+)/i',
  701. '<span class="log5">Command line compressor$1</span>: <span class="log4">$2</span>',
  702. $Log,
  703. 1,
  704. $Count
  705. );
  706. $Log = preg_replace(
  707. "/Additional command line options([^\n]{70,110} )/",
  708. "Additional command line options$1<br>",
  709. $Log
  710. );
  711. $Log = preg_replace(
  712. '/( *)Additional command line options( +): (.+)\n/i',
  713. '<span class="log5">Additional command line options$2</span>: <span class="log4">$3</span>' . "\n",
  714. $Log,
  715. 1,
  716. $Count
  717. );
  718. // xld album gain
  719. $Log = preg_replace(
  720. "/All Tracks\s*\n(\s*Album gain\s+:) (.*)?\n(\s*Peak\s+:) (.*)?/i",
  721. "<span class=\"log5\">All Tracks</span>\n<strong>$1 <span class=\"log3\">$2</span>\n" .
  722. "$3 <span class=\"log3\">$4</span></strong>",
  723. $Log,
  724. 1,
  725. $Count
  726. );
  727. if (!$Count && $XLD) {
  728. $this->account('Could not verify album gain');
  729. }
  730. // pre-0.99
  731. $Log = preg_replace(
  732. '/Other options( +):/i',
  733. '<span class="log5">Other options$1</span>:',
  734. $Log,
  735. 1,
  736. $Count
  737. );
  738. $Log = preg_replace(
  739. '/\n( *)Native Win32 interface(.+)/i',
  740. "\n$1<span class=\"log4\">Native Win32 interface$2</span>",
  741. $Log,
  742. 1,
  743. $Count
  744. );
  745. // 0.99
  746. $Log = str_replace(
  747. 'TOC of the extracted CD',
  748. '<span class="log4 log5">TOC of the extracted CD</span>',
  749. $Log
  750. );
  751. $Log = preg_replace(
  752. '/( +)Track( +)\|( +)Start( +)\|( +)Length( +)\|( +)Start sector( +)\|( +)End sector( ?)/i',
  753. '<strong>$0</strong>',
  754. $Log
  755. );
  756. $Log = preg_replace('/-{10,100}/', '<strong>$0</strong>', $Log);
  757. $Log = preg_replace_callback(
  758. '/( +)([0-9]{1,3})( +)\|( +)(([0-9]{1,3}:)?[0-9]{2}[\.:][0-9]{2})( +)\|' .
  759. '( +)(([0-9]{1,3}:)?[0-9]{2}[\.:][0-9]{2})( +)\|( +)([0-9]{1,10})( +)\|' .
  760. '( +)([0-9]{1,10})( +)\n/i',
  761. [
  762. $this,
  763. 'toc'
  764. ],
  765. $Log
  766. );
  767. $Log = str_replace(
  768. 'None of the tracks are present in the AccurateRip database',
  769. '<span class="badish">None of the tracks are present in the AccurateRip database</span>',
  770. $Log
  771. );
  772. $Log = str_replace(
  773. 'Disc not found in AccurateRip DB.',
  774. '<span class="badish">Disc not found in AccurateRip DB.</span>',
  775. $Log
  776. );
  777. $Log = preg_replace('/No errors occurr?ed/i', '<span class="good">No errors occurred</span>', $Log);
  778. $Log = preg_replace("/(There were errors) ?\n/i", "<span class=\"bad\">$1</span>\n", $Log);
  779. $Log = preg_replace("/(Some inconsistencies found) ?\n/i", "<span class=\"badish\">$1</span>\n", $Log);
  780. $Log = preg_replace('/End of status report/i', '<span class="good">End of status report</span>', $Log);
  781. $Log = preg_replace(
  782. '/Track(\s*)Ripping Status(\s*)\[Disc ID: ([0-9a-f]{8}-[0-9a-f]{8})\]/i',
  783. '<strong>Track</strong>$1<strong>Ripping Status</strong>$2<strong>Disc ID: </strong>' .
  784. '<span class="log1">$3</span>',
  785. $Log
  786. );
  787. $Log = preg_replace('/(All Tracks Accurately Ripped\.?)/i', '<span class="good">$1</span>', $Log);
  788. $Log = preg_replace("/\d+ track.* +accurately ripped\.? *\n/i", '<span class="good">$0</span>', $Log);
  789. $Log = preg_replace(
  790. "/\d+ track.* +not present in the AccurateRip database\.? *\n/i",
  791. '<span class="badish">$0</span>',
  792. $Log
  793. );
  794. $Log = preg_replace("/\d+ track.* +canceled\.? *\n/i", '<span class="bad">$0</span>', $Log);
  795. $Log = preg_replace(
  796. "/\d+ track.* +could not be verified as accurate\.? *\n/i",
  797. '<span class="badish">$0</span>',
  798. $Log
  799. );
  800. $Log = preg_replace(
  801. "/Some tracks could not be verified as accurate\.? *\n/i",
  802. '<span class="badish">$0</span>',
  803. $Log
  804. );
  805. $Log = preg_replace(
  806. "/No tracks could be verified as accurate\.? *\n/i",
  807. '<span class="badish">$0</span>',
  808. $Log
  809. );
  810. $Log = preg_replace("/You may have a different pressing.*\n/i", '<span class="goodish">$0</span>', $Log);
  811. //xld accurip summary
  812. $Log = preg_replace_callback("/(Track +\d+ +: +)(OK +)\(A?R?\d?,? ?confidence +(\d+).*?\)(.*)\n/i", array(
  813. $this,
  814. 'arSummaryConfXld'
  815. ), $Log);
  816. $Log = preg_replace_callback("/(Track +\d+ +: +)(NG|Not Found).*?\n/i", array(
  817. $this,
  818. 'arSummaryConfXld'
  819. ), $Log);
  820. $Log = preg_replace( //Status line
  821. "/( *.{2} ?)(\d+ track\(s\).*)\n/i",
  822. "$1<span class=\"log4\">$2</span>\n",
  823. $Log,
  824. 1
  825. );
  826. //(..) may need additional entries
  827. //accurip summary (range)
  828. $Log = preg_replace(
  829. "/\n( *AccurateRip summary\.?)/i",
  830. "\n<span class=\"log4 log5\">$1</span>",
  831. $Log
  832. );
  833. $Log = preg_replace_callback(
  834. "/(Track +\d+ +.*?accurately ripped\.? *)(\(confidence +)(\d+)\)(.*)\n/i",
  835. [
  836. $this,
  837. 'arSummaryConf'
  838. ],
  839. $Log
  840. );
  841. $Log = preg_replace(
  842. "/(Track +\d+ +.*?in database *)\n/i",
  843. "<span class=\"badish\">$1</span>\n",
  844. $Log,
  845. -1,
  846. $Count
  847. );
  848. if ($Count) {
  849. $this->ARSummary['bad'] = $Count;
  850. }
  851. $Log = preg_replace(
  852. "/(Track +\d+ +.*?(could not|cannot) be verified as accurate.*)\n/i",
  853. "<span class=\"badish\">$1</span>\n",
  854. $Log,
  855. -1,
  856. $Count
  857. );
  858. if ($Count) {
  859. $this->ARSummary['bad'] = $Count;
  860. } //don't mind the actual count
  861. //range rip
  862. $Log = preg_replace("/\n( *Selected range)/i", "\n<span class=\"bad\">$1</span>", $Log, 1, $Range1);
  863. $Log = preg_replace(
  864. '/\n( *Range status and errors)/i',
  865. "\n<span class=\"bad\">$1</span>",
  866. $Log,
  867. 1,
  868. $Range2
  869. );
  870. if ($Range1 || $Range2) {
  871. $this->Range = 1;
  872. $this->account('Range rip detected', 30);
  873. }
  874. $FormattedTrackListing = '';
  875. //------ Handle individual tracks ------//
  876. if (!$this->Range) {
  877. preg_match('/\nTrack( +)([0-9]{1,3})([^<]+)/i', $Log, $Matches);
  878. $TrackListing = $Matches[0];
  879. $FullTracks = preg_split('/\nTrack( +)([0-9]{1,3})/i', $TrackListing, -1, PREG_SPLIT_DELIM_CAPTURE);
  880. array_shift($FullTracks);
  881. $TrackBodies = preg_split('/\nTrack( +)([0-9]{1,3})/i', $TrackListing, -1);
  882. array_shift($TrackBodies);
  883. //------ Range rip ------//
  884. } else {
  885. preg_match('/\n( +)Filename +(.*)([^<]+)/i', $Log, $Matches);
  886. $TrackListing = $Matches[0];
  887. $FullTracks = preg_split('/\n( +)Filename +(.*)/i', $TrackListing, -1, PREG_SPLIT_DELIM_CAPTURE);
  888. array_shift($FullTracks);
  889. $TrackBodies = preg_split('/\n( +)Filename +(.*)/i', $TrackListing, -1);
  890. array_shift($TrackBodies);
  891. }
  892. $Tracks = array();
  893. foreach ($TrackBodies as $Key => $TrackBody) {
  894. // The number of spaces between 'Track' and the number, to keep formatting intact
  895. $Spaces = $FullTracks[($Key * 3)];
  896. // Track number
  897. $TrackNumber = $FullTracks[($Key * 3) + 1];
  898. $this->TrackNumber = $TrackNumber;
  899. // How much to decrease the overall score by, if this track fails and
  900. // no attempt at recovery is made later on
  901. $this->DecreaseScoreTrack = 0;
  902. // List of things that went wrong to add to $this->Bad if this track fails and
  903. // no attempt at recovery is made later on
  904. $this->BadTrack = array();
  905. // The track number is stripped in the preg_split, let's bring it back, eh?
  906. if (!$this->Range) {
  907. $TrackBody = '<span class="log5">Track</span>' . $Spaces . '<span class="log4 log1">' .
  908. $TrackNumber . '</span>' . $TrackBody;
  909. } else {
  910. $TrackBody = $Spaces . '<span class="log5">Filename</span> <span class="log4 log3">' .
  911. $TrackNumber . '</span>' . $TrackBody;
  912. }
  913. /* match newline for xld multifile encodes */
  914. $TrackBody = preg_replace(
  915. '/Filename ((.+)?\.(wav|flac|ape))\n/is',
  916. "<span class=\"log4\">Filename <span class=\"log3\">$1</span></span>\n",
  917. $TrackBody,
  918. -1,
  919. $Count
  920. );
  921. if (!$Count && !$this->Range) {
  922. $this->accountTrack('Could not verify filename', 1);
  923. }
  924. // xld track gain
  925. $TrackBody = preg_replace(
  926. "/( *Track gain\s+:) (.*)?\n(\s*Peak\s+:) (.*)?/i",
  927. "<strong>$1 <span class=\"log3\">$2</span>\n$3 <span class=\"log3\">$4</span></strong>",
  928. $TrackBody,
  929. -1,
  930. $Count
  931. );
  932. $TrackBody = preg_replace(
  933. '/( +)(Statistics *)\n/i',
  934. "$1<span class=\"log5\">$2</span>\n",
  935. $TrackBody,
  936. -1,
  937. $Count
  938. );
  939. $TrackBody = preg_replace_callback('/(Read error)( +:) (\d+)/i', array(
  940. $this,
  941. 'xldStat'
  942. ), $TrackBody, -1, $Count);
  943. if (!$Count && $XLD) {
  944. $this->accountTrack('Could not verify read errors');
  945. }
  946. $TrackBody = preg_replace_callback('/(Skipped \(treated as error\))( +:) (\d+)/i', array(
  947. $this,
  948. 'xldStat'
  949. ), $TrackBody, -1, $Count);
  950. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  951. $this->accountTrack('Could not verify skipped errors');
  952. }
  953. $TrackBody = preg_replace_callback('/(Edge jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  954. $this,
  955. 'xldStat'
  956. ), $TrackBody, -1, $Count);
  957. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  958. $this->accountTrack('Could not verify edge jitter errors');
  959. }
  960. $TrackBody = preg_replace_callback('/(Atom jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  961. $this,
  962. 'xldStat'
  963. ), $TrackBody, -1, $Count);
  964. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  965. $this->accountTrack('Could not verify atom jitter errors');
  966. }
  967. $TrackBody = preg_replace_callback( //xld secure ripper
  968. '/(Jitter error \(maybe fixed\))( +:) (\d+)/i',
  969. array(
  970. $this,
  971. 'xldStat'
  972. ),
  973. $TrackBody,
  974. -1,
  975. $Count
  976. );
  977. if (!$Count && $XLD && $this->XLDSecureRipper) {
  978. $this->accountTrack('Could not verify jitter errors');
  979. }
  980. $TrackBody = preg_replace_callback( //xld secure ripper
  981. '/(Retry sector count)( +:) (\d+)/i',
  982. array(
  983. $this,
  984. 'xldStat'
  985. ),
  986. $TrackBody,
  987. -1,
  988. $Count
  989. );
  990. if (!$Count && $XLD && $this->XLDSecureRipper) {
  991. $this->accountTrack('Could not verify retry sector count');
  992. }
  993. $TrackBody = preg_replace_callback( //xld secure ripper
  994. '/(Damaged sector count)( +:) (\d+)/i',
  995. array(
  996. $this,
  997. 'xldStat'
  998. ),
  999. $TrackBody,
  1000. -1,
  1001. $Count
  1002. );
  1003. if (!$Count && $XLD && $this->XLDSecureRipper) {
  1004. $this->accountTrack('Could not verify damaged sector count');
  1005. }
  1006. $TrackBody = preg_replace_callback('/(Drift error \(maybe fixed\))( +:) (\d+)/i', array(
  1007. $this,
  1008. 'xldStat'
  1009. ), $TrackBody, -1, $Count);
  1010. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  1011. $this->accountTrack('Could not verify drift errors');
  1012. }
  1013. $TrackBody = preg_replace_callback('/(Dropped bytes error \(maybe fixed\))( +:) (\d+)/i', array(
  1014. $this,
  1015. 'xldStat'
  1016. ), $TrackBody, -1, $Count);
  1017. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  1018. $this->accountTrack('Could not verify dropped bytes errors');
  1019. }
  1020. $TrackBody = preg_replace_callback(
  1021. '/(Duplicated bytes error \(maybe fixed\))( +:) (\d+)/i',
  1022. [
  1023. $this,
  1024. 'xldStat'
  1025. ],
  1026. $TrackBody,
  1027. -1,
  1028. $Count
  1029. );
  1030. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  1031. $this->accountTrack('Could not verify duplicated bytes errors');
  1032. }
  1033. $TrackBody = preg_replace_callback('/(Inconsistency in error sectors)( +:) (\d+)/i', array(
  1034. $this,
  1035. 'xldStat'
  1036. ), $TrackBody, -1, $Count);
  1037. if (!$Count && $XLD && !$this->XLDSecureRipper) {
  1038. $this->accountTrack('Could not verify inconsistent error sectors');
  1039. }
  1040. $TrackBody = preg_replace(
  1041. "/(List of suspicious positions +)(: *\n?)(( *.* +\d{2}:\d{2}:\d{2} *\n)+)/i",
  1042. '<span class="bad">$1</span><strong>$2</strong><span class="bad">$3</span></span>',
  1043. $TrackBody,
  1044. -1,
  1045. $Count
  1046. );
  1047. if ($Count) {
  1048. $this->accountTrack('Suspicious position(s) found', 20);
  1049. }
  1050. $TrackBody = preg_replace(
  1051. '/Suspicious position( +)([0-9]:[0-9]{2}:[0-9]{2})/i',
  1052. '<span class="bad">Suspicious position$1<span class="log4">$2</span></span>',
  1053. $TrackBody,
  1054. -1,
  1055. $Count
  1056. );
  1057. if ($Count) {
  1058. $this->accountTrack('Suspicious position(s) found', 20);
  1059. }
  1060. $TrackBody = preg_replace(
  1061. '/Timing problem( +)([0-9]:[0-9]{2}:[0-9]{2})/i',
  1062. '<span class="bad">Timing problem$1<span class="log4">$2</span></span>',
  1063. $TrackBody,
  1064. -1,
  1065. $Count
  1066. );
  1067. if ($Count) {
  1068. $this->accountTrack('Timing problem(s) found', 20);
  1069. }
  1070. $TrackBody = preg_replace(
  1071. '/Missing samples/i',
  1072. '<span class="bad">Missing samples</span>',
  1073. $TrackBody,
  1074. -1,
  1075. $Count
  1076. );
  1077. if ($Count) {
  1078. $this->accountTrack('Missing sample(s) found', 20);
  1079. }
  1080. $TrackBody = preg_replace(
  1081. '/Copy aborted/i',
  1082. '<span class="bad">Copy aborted</span>',
  1083. $TrackBody,
  1084. -1,
  1085. $Count
  1086. );
  1087. if ($Count) {
  1088. $Aborted = true;
  1089. $this->accountTrack('Copy aborted', 100);
  1090. } else {
  1091. $Aborted = false;
  1092. }
  1093. $TrackBody = preg_replace(
  1094. '/Pre-gap length( +|\s+:\s+)([0-9]{1,2}:[0-9]{2}:[0-9]{2}.?[0-9]{0,2})/i',
  1095. '<span class="log4">Pre-gap length$1<span class="log3">$2</span></span>',
  1096. $TrackBody,
  1097. -1,
  1098. $Count
  1099. );
  1100. $TrackBody = preg_replace(
  1101. '/Peak level ([0-9]{1,3}\.[0-9] %)/i',
  1102. '<span class="log4">Peak level <span class="log3">$1</span></span>',
  1103. $TrackBody,
  1104. -1,
  1105. $Count
  1106. );
  1107. $TrackBody = preg_replace(
  1108. '/Extraction speed ([0-9]{1,3}\.[0-9]{1,} X)/i',
  1109. '<span class="log4">Extraction speed <span class="log3">$1</span></span>',
  1110. $TrackBody,
  1111. -1,
  1112. $Count
  1113. );
  1114. $TrackBody = preg_replace(
  1115. '/Track quality ([0-9]{1,3}\.[0-9] %)/i',
  1116. '<span class="log4">Track quality <span class="log3">$1</span></span>',
  1117. $TrackBody,
  1118. -1,
  1119. $Count
  1120. );
  1121. $TrackBody = preg_replace(
  1122. '/Range quality\s+([0-9]{1,3}\.[0-9] %)/i',
  1123. '<span class="log4">Range quality <span class="log3">$1</span></span>',
  1124. $TrackBody,
  1125. -1,
  1126. $Count
  1127. );
  1128. $TrackBody = preg_replace(
  1129. '/CRC32 hash \(skip zero\)(\s*:) ([0-9A-F]{8})/i',
  1130. '<span class="log4">CRC32 hash (skip zero)$1<span class="log3"> $2</span></span>',
  1131. $TrackBody,
  1132. -1,
  1133. $Count
  1134. );
  1135. $TrackBody = preg_replace_callback(
  1136. '/Test CRC ([0-9A-F]{8})\n(\s*)Copy CRC ([0-9A-F]{8})/i',
  1137. [
  1138. $this,
  1139. 'testCopy'
  1140. ],
  1141. $TrackBody,
  1142. -1,
  1143. $EACTC
  1144. );
  1145. $TrackBody = preg_replace_callback(
  1146. '/CRC32 hash \(test run\)(\s*:) ([0-9A-F]{8})\n(\s*)CRC32 hash(\s+:) ([0-9A-F]{8})/i',
  1147. [
  1148. $this,
  1149. 'testCopy'
  1150. ],
  1151. $TrackBody,
  1152. -1,
  1153. $XLDTC
  1154. );
  1155. if (!$EACTC && !$XLDTC && !$Aborted) {
  1156. $this->account('Test and copy was not used', 10);
  1157. if (!$this->SecureMode) {
  1158. if ($EAC) {
  1159. $Msg = 'Rip was not done in Secure mode, and T+C was not used - as a result, ' .
  1160. 'we cannot verify the authenticity of the rip (-40 points)';
  1161. } else {
  1162. $Msg = 'Rip was not done with Secure Ripper / in CDParanoia mode, and T+C was not used ' .
  1163. '- as a result, we cannot verify the authenticity of the rip (-40 points)';
  1164. }
  1165. if (!in_array($Msg, $this->Details)) {
  1166. $this->Score -= 40;
  1167. $this->Details[] = $Msg;
  1168. }
  1169. }
  1170. }
  1171. $TrackBody = preg_replace(
  1172. '/Copy CRC ([0-9A-F]{8})/i',
  1173. '<span class="log4">Copy CRC <span class="log3">$1</span></span>',
  1174. $TrackBody,
  1175. -1,
  1176. $Count
  1177. );
  1178. $TrackBody = preg_replace(
  1179. '/CRC32 hash(\s*:) ([0-9A-F]{8})/i',
  1180. '<span class="log4">CRC32 hash$1<span class="goodish"> $2</span></span>',
  1181. $TrackBody,
  1182. -1,
  1183. $Count
  1184. );
  1185. $TrackBody = str_replace(
  1186. 'Track not present in AccurateRip database',
  1187. '<span class="badish">Track not present in AccurateRip database</span>',
  1188. $TrackBody
  1189. );
  1190. $TrackBody = preg_replace(
  1191. '/Accurately ripped( +)\(confidence ([0-9]+)\)( +)(\[[0-9A-F]{8}\])/i',
  1192. '<span class="good">Accurately ripped$1(confidence $2)$3$4</span>',
  1193. $TrackBody,
  1194. -1,
  1195. $Count
  1196. );
  1197. $TrackBody = preg_replace(
  1198. "/Cannot be verified as accurate +\(.*/i",
  1199. '<span class="badish">$0</span>',
  1200. $TrackBody,
  1201. -1,
  1202. $Count
  1203. );
  1204. //xld ar
  1205. $TrackBody = preg_replace_callback(
  1206. '/AccurateRip signature( +): ([0-9A-F]{8})\n(.*?)(Accurately ripped\!?)' .
  1207. '( +\(A?R?\d?,? ?confidence )([0-9]+\))/i',
  1208. [
  1209. $this,
  1210. 'arXld'
  1211. ],
  1212. $TrackBody,
  1213. -1,
  1214. $Count
  1215. );
  1216. $TrackBody = preg_replace(
  1217. '/AccurateRip signature( +): ([0-9A-F]{8})\n(.*?)(Rip may not be accurate\.?)(.*?)/i',
  1218. "<span class=\"log4\">AccurateRip signature$1: <span class=\"badish\">$2</span></span>\n" .
  1219. "$3<span class=\"badish\">$4$5</span>",
  1220. $TrackBody,
  1221. -1,
  1222. $Count
  1223. );
  1224. $TrackBody = preg_replace(
  1225. '/(Rip may not be accurate\.?)(.*?)/i',
  1226. "<span class=\"badish\">$1$2</span>",
  1227. $TrackBody,
  1228. -1,
  1229. $Count
  1230. );
  1231. $TrackBody = preg_replace(
  1232. '/AccurateRip signature( +): ([0-9A-F]{8})\n(.*?)' .
  1233. '(Track not present in AccurateRip database\.?)(.*?)/i',
  1234. "<span class=\"log4\">AccurateRip signature$1: <span class=\"badish\">$2</span></span>\n" .
  1235. "$3<span class=\"badish\">$4$5</span>",
  1236. $TrackBody,
  1237. -1,
  1238. $Count
  1239. );
  1240. $TrackBody = preg_replace(
  1241. "/\(matched[ \w]+;\n *calculated[ \w]+;\n[ \w]+signature[ \w:]+\)/i",
  1242. "<span class=\"goodish\">$0</span>",
  1243. $TrackBody,
  1244. -1,
  1245. $Count
  1246. );
  1247. //ar track + conf
  1248. preg_match('/Accurately ripped\!? +\(A?R?\d?,? ?confidence ([0-9]+)\)/i', $TrackBody, $matches);
  1249. if ($matches) {
  1250. $this->ARTracks[$TrackNumber] = $matches[1];
  1251. } else {
  1252. $this->ARTracks[$TrackNumber] = 0;
  1253. } //no match - no boost
  1254. $TrackBody = str_replace('Copy finished', '<span class="log3">Copy finished</span>', $TrackBody);
  1255. $TrackBody = preg_replace('/Copy OK/i', '<span class="good">Copy OK</span>', $TrackBody, -1, $Count);
  1256. $Tracks[$TrackNumber] = array(
  1257. 'number' => $TrackNumber,
  1258. 'spaces' => $Spaces,
  1259. 'text' => $TrackBody,
  1260. 'decreasescore' => $this->DecreaseScoreTrack,
  1261. 'bad' => $this->BadTrack
  1262. );
  1263. $FormattedTrackListing .= "\n" . $TrackBody;
  1264. $this->Tracks[$LogArrayKey][$TrackNumber] = $Tracks[$TrackNumber];
  1265. }
  1266. unset($Tracks);
  1267. $Log = str_replace($TrackListing, $FormattedTrackListing, $Log);
  1268. $Log = str_replace('<br>', "\n", $Log);
  1269. //xld all tracks statistics
  1270. $Log = preg_replace('/( +)?(All tracks *)\n/i', "$1<span class=\"log5\">$2</span>\n", $Log, 1);
  1271. $Log = preg_replace('/( +)(Statistics *)\n/i', "$1<span class=\"log5\">$2</span>\n", $Log, 1);
  1272. $Log = preg_replace_callback('/(Read error)( +:) (\d+)/i', array(
  1273. $this,
  1274. 'xldAllStat'
  1275. ), $Log, 1);
  1276. $Log = preg_replace_callback('/(Skipped \(treated as error\))( +:) (\d+)/i', array(
  1277. $this,
  1278. 'xldAllStat'
  1279. ), $Log, 1);
  1280. $Log = preg_replace_callback('/(Jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  1281. $this,
  1282. 'xldAllStat'
  1283. ), $Log, 1);
  1284. $Log = preg_replace_callback('/(Edge jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  1285. $this,
  1286. 'xldAllStat'
  1287. ), $Log, 1);
  1288. $Log = preg_replace_callback('/(Atom jitter error \(maybe fixed\))( +:) (\d+)/i', array(
  1289. $this,
  1290. 'xldAllStat'
  1291. ), $Log, 1);
  1292. $Log = preg_replace_callback('/(Drift error \(maybe fixed\))( +:) (\d+)/i', array(
  1293. $this,
  1294. 'xldAllStat'
  1295. ), $Log, 1);
  1296. $Log = preg_replace_callback('/(Dropped bytes error \(maybe fixed\))( +:) (\d+)/i', array(
  1297. $this,
  1298. 'xldAllStat'
  1299. ), $Log, 1);
  1300. $Log = preg_replace_callback('/(Duplicated bytes error \(maybe fixed\))( +:) (\d+)/i', array(
  1301. $this,
  1302. 'xldAllStat'
  1303. ), $Log, 1);
  1304. $Log = preg_replace_callback('/(Retry sector count)( +:) (\d+)/i', array(
  1305. $this,
  1306. 'xldAllStat'
  1307. ), $Log, 1);
  1308. $Log = preg_replace_callback('/(Damaged sector count)( +:) (\d+)/i', array(
  1309. $this,
  1310. 'xldAllStat'
  1311. ), $Log, 1);
  1312. //end xld all tracks statistics
  1313. $this->logs[$LogArrayKey] = $Log;
  1314. $this->checkTracks($LogArrayKey);
  1315. if ($this->NonSecureMode) { #non-secure mode
  1316. $this->account($this->NonSecureMode . ' mode was used', 20);
  1317. }
  1318. $this->ARTracks = array();
  1319. $this->ARSummary = array();
  1320. $this->SecureMode = true;
  1321. $this->NonSecureMode = null;
  1322. } //end log loop
  1323. $FinalTracks = [];
  1324. foreach ($this->Tracks as $LogArrayKey => $Tracks) {
  1325. foreach ($Tracks as $Number => $Track) {
  1326. $FinalTracks[$Number] = $Track;
  1327. }
  1328. }
  1329. foreach ($FinalTracks as $Track) {
  1330. if ($Track['decreasescore']) {
  1331. $this->Score -= $Track['decreasescore'];
  1332. }
  1333. if (count($Track['bad']) > 0) {
  1334. $this->Details = array_merge($this->Details, $Track['bad']);
  1335. }
  1336. }
  1337. unset($FinalTracks);
  1338. unset($this->Tracks);
  1339. $this->log = implode($this->logs);
  1340. if (strlen($this->log) === 0) {
  1341. $this->Score = 0;
  1342. $this->account('Unrecognized log file! Feel free to report for manual review.');
  1343. }
  1344. if ($this->Combined) {
  1345. array_unshift($this->Details, "Combined Log (" . $this->Combined . ")");
  1346. } //combined log msg
  1347. }
  1348. // Callback functions
  1349. private function drive($Matches)
  1350. {
  1351. if (in_array(trim($Matches[2]), $this->FakeDrives)) {
  1352. $this->account('Virtual drive used: ' . $Matches[2], 20, false, false, false);
  1353. return "<span class=\"log5\">Used Drive$Matches[1]</span>: <span class=\"bad\">$Matches[2]</span>";
  1354. }
  1355. $DriveName = $Matches[2];
  1356. $this->getDrives($DriveName);
  1357. if (count($this->Drives) > 0) {
  1358. $Class = 'good';
  1359. $this->DriveFound = true;
  1360. } else {
  1361. $Class = 'badish';
  1362. $Matches[2] .= ' (not found in database)';
  1363. }
  1364. return "<span class=\"log5\">Used Drive$Matches[1]</span>: <span class=\"$Class\">$Matches[2]</span>";
  1365. }
  1366. private function getDrives($DriveName)
  1367. {
  1368. // Necessary transformations to get what the drives report themselves to match up into
  1369. // what is from the AccurateRIP DB
  1370. $DriveName = str_replace('JLMS', 'Lite-ON', $DriveName);
  1371. $DriveName = preg_replace('/TSSTcorp(BD|CD|DVD)/', 'TSSTcorp \1', $DriveName);
  1372. $DriveName = preg_replace('/HL-DT-ST(BD|CD|DVD)/', 'HL-DT-ST \1', $DriveName);
  1373. $DriveName = str_replace('HL-DT-ST', 'LG Electronics', $DriveName);
  1374. $DriveName = str_replace(array('Matshita', 'MATSHITA'), 'Panasonic', $DriveName);
  1375. $DriveName = preg_replace('/\s+-\s/', ' ', $DriveName);
  1376. $DriveName = preg_replace('/\s+/', ' ', $DriveName);
  1377. $DriveName = preg_replace('/\(revision [a-zA-Z0-9\.\,\-]*\)/', '', $DriveName);
  1378. $DriveName = preg_replace('/ Adapter.*$/', '', $DriveName);
  1379. $DriveName = strtolower($DriveName);
  1380. $MatchedDrives = [];
  1381. for ($i = 0; $i < 5; $i++) {
  1382. $MatchedDrives[$i] = ['drives' => [], 'offsets' => []];
  1383. }
  1384. foreach ($this->AllDrives as [$Drive, $Offset]) {
  1385. $Distance = levenshtein($Drive, $DriveName);
  1386. if ($Distance < 5) {
  1387. $MatchedDrives[$Distance]['drives'][] = $Drive;
  1388. $MatchedDrives[$Distance]['offsets'][] = preg_replace('/[^0-9]/s', '', (string) $Offset);
  1389. }
  1390. }
  1391. foreach ($MatchedDrives as $Match) {
  1392. if (count($Match['drives']) > 0) {
  1393. $this->Drives = $Match['drives'];
  1394. $this->Offsets = $Match['offsets'];
  1395. break;
  1396. }
  1397. }
  1398. }
  1399. private function mediaTypeXld($Matches)
  1400. {
  1401. // Pressed CD
  1402. if (trim($Matches[2]) == "Pressed CD") {
  1403. $Class = 'good';
  1404. } else { // CD-R etc.; not necessarily "bad" (e.g. commercial CD-R)
  1405. $Class = 'badish';
  1406. $this->account('Not a pressed cd', false, false, true, true);
  1407. }
  1408. return "<span class=\"log5\">Media type$Matches[1]</span>: <span class=\"$Class\">$Matches[2]</span>";
  1409. }
  1410. private function readMode($Matches)
  1411. {
  1412. if ($Matches[2] == 'Secure') {
  1413. $Class = 'good';
  1414. } else {
  1415. $this->SecureMode = false;
  1416. $this->NonSecureMode = $Matches[2];
  1417. $Class = 'bad';
  1418. }
  1419. $Str = '<span class="log5">Read mode' . $Matches[1] . '</span>: ' .
  1420. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1421. if ($Matches[3]) {
  1422. $Str .= '<span class="log4">' . $Matches[3] . '</span>';
  1423. }
  1424. return $Str;
  1425. }
  1426. private function cdparanoiaModeXld($Matches)
  1427. {
  1428. if (substr($Matches[2], 0, 3) == 'YES') {
  1429. $Class = 'good';
  1430. } else {
  1431. $this->SecureMode = false;
  1432. $Class = 'bad';
  1433. }
  1434. return '<span class="log5">Use cdparanoia mode' . $Matches[1] . '</span>: ' .
  1435. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1436. }
  1437. private function ripperModeXld($Matches)
  1438. {
  1439. if (substr($Matches[2], 0, 10) == 'CDParanoia') {
  1440. $Class = 'good';
  1441. } elseif ($Matches[2] == "XLD Secure Ripper") {
  1442. $Class = 'good';
  1443. $this->XLDSecureRipper = true;
  1444. } else {
  1445. $this->SecureMode = false;
  1446. $Class = 'bad';
  1447. }
  1448. return '<span class="log5">Ripper mode' . $Matches[1] . '</span>: ' .
  1449. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1450. }
  1451. private function arXld($Matches)
  1452. {
  1453. if (strpos(strtolower($Matches[4]), 'accurately ripped') != -1) {
  1454. $conf = substr($Matches[6], 0, -1);
  1455. if ((int) $conf < 2) {
  1456. $Class = 'goodish';
  1457. } else {
  1458. $Class = 'good';
  1459. }
  1460. } else {
  1461. $Class = 'badish';
  1462. }
  1463. return "<span class=\"log4\">AccurateRip signature$Matches[1]: " .
  1464. "<span class=\"$Class\">$Matches[2]</span></span>\n" .
  1465. "$Matches[3]<span class=\"$Class\">$Matches[4]$Matches[5]$Matches[6]</span>";
  1466. }
  1467. private function arSummaryConfXld($Matches)
  1468. {
  1469. if (strtolower(trim($Matches[2])) == 'ok') {
  1470. if ($Matches[3] < 2) {
  1471. $Class = 'goodish';
  1472. } else {
  1473. $Class = 'good';
  1474. }
  1475. } else {
  1476. $Class = 'badish';
  1477. }
  1478. return "$Matches[1]<span class =\"$Class\">" . substr($Matches[0], strlen($Matches[1])) . "</span>";
  1479. }
  1480. private function arSummaryConf($Matches)
  1481. {
  1482. if ($Matches[3] < 2) {
  1483. $Class = 'goodish';
  1484. $this->ARSummary['goodish'][] = $Matches[3];
  1485. } else {
  1486. $Class = 'good';
  1487. $this->ARSummary['good'][] = $Matches[3];
  1488. }
  1489. return "<span class =\"$Class\">$Matches[0]</span>";
  1490. }
  1491. private function maxRetryCount($Matches)
  1492. {
  1493. if ($Matches[2] >= 10) {
  1494. $Class = 'goodish';
  1495. } else {
  1496. $Class = 'badish';
  1497. $this->account('Low "max retry count" (potentially bad setting)');
  1498. }
  1499. return '<span class="log5">Max retry count' . $Matches[1] . '</span>: ' .
  1500. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1501. }
  1502. private function accurateStream($Matches)
  1503. {
  1504. if ($Matches[2] == 'Yes') {
  1505. $Class = 'good';
  1506. } else {
  1507. $Class = 'bad';
  1508. $this->account('"Utilize accurate stream" should be yes', 20);
  1509. }
  1510. return '<span class="log5">Utilize accurate stream' . $Matches[1] . '</span>: ' .
  1511. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1512. }
  1513. private function accurateStreamEacPre9($Matches)
  1514. {
  1515. if (strtolower($Matches[1]) != 'no ') {
  1516. $Class = 'good';
  1517. } else {
  1518. $Class = 'bad';
  1519. $this->account('"accurate stream" should be yes', 20);
  1520. }
  1521. return ', <span class="' . $Class . '">' . $Matches[1] . 'accurate stream</span>';
  1522. }
  1523. private function defeatAudioCache($Matches)
  1524. {
  1525. if ($Matches[2] == 'Yes') {
  1526. $Class = 'good';
  1527. } else {
  1528. $Class = 'bad';
  1529. $this->account('"Defeat audio cache" should be yes', 10);
  1530. }
  1531. return '<span class="log5">Defeat audio cache' . $Matches[1] . '</span>: ' .
  1532. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1533. }
  1534. private function defeatAudioCacheEacPre99($Matches)
  1535. {
  1536. if (strtolower($Matches[1]) != 'no ') {
  1537. $Class = 'good';
  1538. } else {
  1539. $Class = 'bad';
  1540. $this->account('Audio cache not disabled', 10);
  1541. }
  1542. return ' <span class="' . $Class . '">' . $Matches[1] . 'disable cache</span>';
  1543. }
  1544. private function defeatAudioCacheXld($Matches)
  1545. {
  1546. if (substr($Matches[2], 0, 2) == 'OK' || substr($Matches[2], 0, 3) == 'YES') {
  1547. $Class = 'good';
  1548. } else {
  1549. $Class = 'bad';
  1550. $this->account('"Disable audio cache" should be yes/ok', 10);
  1551. }
  1552. return '<span class="log5">Disable audio cache' . $Matches[1] . '</span>: ' .
  1553. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1554. }
  1555. private function c2Pointers($Matches)
  1556. {
  1557. if (strtolower($Matches[2]) == 'yes') {
  1558. $Class = 'bad';
  1559. $this->account('C2 pointers were used', 10);
  1560. } else {
  1561. $Class = 'good';
  1562. }
  1563. return '<span class="log5">Make use of C2 pointers' . $Matches[1] . '</span>: ' .
  1564. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1565. }
  1566. private function c2PointersEacPre99($Matches)
  1567. {
  1568. if (strtolower($Matches[1]) == 'no ') {
  1569. $Class = 'good';
  1570. } else {
  1571. $Class = 'bad';
  1572. $this->account('C2 pointers were used', 10);
  1573. }
  1574. return 'with <span class="' . $Class . '">' . $Matches[1] . 'C2</span>';
  1575. }
  1576. private function readOffset($Matches)
  1577. {
  1578. if ($this->DriveFound == true) {
  1579. if (in_array($Matches[2], $this->Offsets)) {
  1580. $Class = 'good';
  1581. } else {
  1582. $Class = 'bad';
  1583. $Msg = 'Incorrect read offset for drive. Correct offsets are: ' . implode(', ', $this->Offsets) .
  1584. ' (Checked against the following drive(s): ' . implode(', ', $this->Drives) . ')';
  1585. $this->account($Msg, 5, false, false, false);
  1586. }
  1587. } else {
  1588. if ($Matches[2] == 0) {
  1589. $Class = 'bad';
  1590. $Msg = 'The drive was not found in the database, so we cannot determine the correct read offset. ' .
  1591. 'However, the read offset in this case was 0, which is almost never correct. As such, we are ' .
  1592. 'assuming that the offset is incorrect';
  1593. $this->account($Msg, 5, false, false, false);
  1594. } else {
  1595. $Class = 'badish';
  1596. }
  1597. }
  1598. return '<span class="log5">Read offset correction' . $Matches[1] . '</span>: ' .
  1599. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1600. }
  1601. private function fillOffsetSamples($Matches)
  1602. {
  1603. if ($Matches[2] == 'Yes') {
  1604. $Class = 'good';
  1605. } else {
  1606. $Class = 'bad';
  1607. $this->account('Does not fill up missing offset samples with silence', 5, false, false, false);
  1608. }
  1609. return '<span class="log5">Fill up missing offset samples with silence' . $Matches[1] .
  1610. '</span>: <span class="' . $Class . '">' . $Matches[2] . '</span>';
  1611. }
  1612. private function deleteSilentBlocks($Matches)
  1613. {
  1614. if ($Matches[2] == 'Yes') {
  1615. $Class = 'bad';
  1616. $this->account('Deletes leading and trailing silent blocks', 5, false, false, false);
  1617. } else {
  1618. $Class = 'good';
  1619. }
  1620. return '<span class="log5">Delete leading and trailing silent blocks' .
  1621. $Matches[1] . $Matches[2] . '</span>: <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1622. }
  1623. private function nullSamples($Matches)
  1624. {
  1625. if ($Matches[2] == 'Yes') {
  1626. $Class = 'good';
  1627. } else {
  1628. $Class = 'bad';
  1629. $this->account('Null samples should be used in CRC calculations', 5);
  1630. }
  1631. return '<span class="log5">Null samples used in CRC calculations' . $Matches[1] . '</span>: ' .
  1632. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1633. }
  1634. private function gapHandling($Matches)
  1635. {
  1636. if (strpos($Matches[2], 'Not detected') !== false) {
  1637. $Class = 'bad';
  1638. $this->account('Gap handling was not detected', 10);
  1639. } elseif (strpos($Matches[2], 'Appended to next track') !== false) {
  1640. $Class = 'bad';
  1641. $this->account('Gap handling should be appended to previous track');
  1642. } elseif (strpos($Matches[2], 'Appended to previous track') !== false) {
  1643. $Class = 'good';
  1644. } else {
  1645. $Class = 'goodish';
  1646. }
  1647. return '<span class="log5">Gap handling' . $Matches[1] . '</span>: ' .
  1648. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1649. }
  1650. private function gapHandlingXld($Matches)
  1651. {
  1652. if (strpos(strtolower($Matches[2]), 'not') !== false) { //?
  1653. $Class = 'bad';
  1654. $this->account('Incorrect gap handling', 10, false, false, false);
  1655. } elseif (
  1656. strpos(strtolower($Matches[2]), 'analyzed') !== false
  1657. && strpos(strtolower($Matches[2]), 'appended') !== false
  1658. ) {
  1659. $Class = 'good';
  1660. } else {
  1661. $Class = 'badish';
  1662. $this->account('Incomplete gap handling', 10, false, false, false);
  1663. }
  1664. return '<span class="log5">Gap status' . $Matches[1] . '</span>: ' .
  1665. '<span class="' . $Class . '">' . $Matches[2] . '</span>';
  1666. }
  1667. private function addId3Tag($Matches)
  1668. {
  1669. if ($Matches[2] == 'Yes') {
  1670. $Class = 'badish';
  1671. $this->account(
  1672. 'ID3 tags should not be added to FLAC files - they are mainly for MP3 files. ' .
  1673. 'FLACs should have vorbis comments for tags instead.',
  1674. 1
  1675. );
  1676. } else {
  1677. $Class = 'good';
  1678. }
  1679. return '<span class="log5">Add ID3 tag' . $Matches[1] . '</span>: <span class="' .
  1680. $Class . '">' . $Matches[2] . '</span>';
  1681. }
  1682. private function testCopy($Matches)
  1683. {
  1684. if ($this->ripper == "EAC") {
  1685. if ($Matches[1] == $Matches[3]) {
  1686. $Class = 'good';
  1687. } else {
  1688. $Class = 'bad';
  1689. $this->accountTrack("CRC mismatch: $Matches[1] and $Matches[3]", 30);
  1690. if (!$this->SecureMode) {
  1691. $this->DecreaseScoreTrack += 20;
  1692. $this->BadTrack[] = 'Rip ' . (($this->Combined) ? " (" . $this->CurrLog . ") " : '') . 'was not ' .
  1693. 'done in Secure mode, and experienced CRC mismatches (-20 points)';
  1694. $this->SecureMode = true;
  1695. }
  1696. }
  1697. return "<span class=\"log4\">Test CRC <span class=\"$Class\">$Matches[1]</span></span>\n" .
  1698. "$Matches[2]<span class=\"log4\">Copy CRC <span class=\"$Class\">$Matches[3]</span></span>";
  1699. } elseif ($this->ripper == "XLD") {
  1700. if ($Matches[2] == $Matches[5]) {
  1701. $Class = 'good';
  1702. } else {
  1703. $Class = 'bad';
  1704. $this->accountTrack("CRC mismatch: $Matches[2] and $Matches[5]", 30);
  1705. if (!$this->SecureMode) {
  1706. $this->DecreaseScoreTrack += 20;
  1707. $this->BadTrack[] = 'Rip ' . (($this->Combined) ? " (" . $this->CurrLog . ") " : '') . 'was not ' .
  1708. 'done with Secure Ripper / in CDParanoia mode, and experienced CRC mismatches (-20 points)';
  1709. $this->SecureMode = true;
  1710. }
  1711. }
  1712. return "<span class=\"log4\">CRC32 hash (test run)$Matches[1] " .
  1713. "<span class=\"$Class\">$Matches[2]</span></span>\n$Matches[3] " .
  1714. "<span class=\"log4\">CRC32 hash$Matches[4] <span class=\"$Class\">$Matches[5]</span></span>";
  1715. }
  1716. }
  1717. private function xldAllStat($Matches)
  1718. {
  1719. $Text = $Matches[1] . $Matches[2];
  1720. if (
  1721. strtolower($Matches[1]) == 'read error'
  1722. || strtolower($Matches[1]) == 'skipped (treated as error)'
  1723. || strtolower($Matches[1]) == 'inconsistency in error sectors'
  1724. || strtolower($Matches[1]) == 'damaged sector count'
  1725. ) {
  1726. if ($Matches[3] == 0) {
  1727. $Class = 'good';
  1728. } else {
  1729. $Class = 'bad';
  1730. }
  1731. return '<span class="log4">' . $Text . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1732. }
  1733. if (
  1734. strtolower($Matches[1]) == 'retry sector count'
  1735. || strtolower($Matches[1]) == 'jitter error (maybe fixed)'
  1736. || strtolower($Matches[1]) == 'edge jitter error (maybe fixed)'
  1737. || strtolower($Matches[1]) == 'atom jitter error (maybe fixed)'
  1738. || strtolower($Matches[1]) == 'drift error (maybe fixed)'
  1739. || strtolower($Matches[1]) == 'dropped bytes error (maybe fixed)'
  1740. || strtolower($Matches[1]) == 'duplicated bytes error (maybe fixed)'
  1741. ) {
  1742. if ($Matches[3] == 0) {
  1743. $Class = 'goodish';
  1744. } else {
  1745. $Class = 'badish';
  1746. }
  1747. return '<span class="log4">' . $Text . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1748. }
  1749. }
  1750. private function xldStat($Matches)
  1751. {
  1752. $Text = $Matches[1] . $Matches[2];
  1753. if (strtolower($Matches[1]) == 'read error') {
  1754. if ($Matches[3] == 0) {
  1755. $Class = 'good';
  1756. } else {
  1757. $Class = 'bad';
  1758. $err = ($Matches[3] > 10) ? 10 : $Matches[3]; //max.
  1759. $this->accountTrack('Read error' . ($Matches[3] == 1 ? '' : 's') . ' detected', $err);
  1760. }
  1761. return '<span class="log4">' . $Text . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1762. }
  1763. if (strtolower($Matches[1]) == 'skipped (treated as error)') {
  1764. if ($Matches[3] == 0) {
  1765. $Class = 'good';
  1766. } else {
  1767. $Class = 'bad';
  1768. $err = ($Matches[3] > 10) ? 10 : $Matches[3]; //max.
  1769. $this->accountTrack('Skipped error' . ($Matches[3] == 1 ? '' : 's') . ' detected', $err);
  1770. }
  1771. return '<span class="log4">' . $Text . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1772. }
  1773. if (strtolower($Matches[1]) == 'inconsistency in error sectors') {
  1774. if ($Matches[3] == 0) {
  1775. $Class = 'good';
  1776. } else {
  1777. $Class = 'bad';
  1778. $err = ($Matches[3] > 10) ? 10 : $Matches[3]; //max.
  1779. $this->accountTrack(
  1780. 'Inconsistenc' . (($Matches[3] == 1) ? 'y' : 'ies') . ' in error sectors detected',
  1781. $err
  1782. );
  1783. }
  1784. return '<span class="log4">' . $Text . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1785. }
  1786. if (strtolower($Matches[1]) == 'damaged sector count') { //xld secure ripper
  1787. if ($Matches[3] == 0) {
  1788. $Class = 'good';
  1789. } else {
  1790. $Class = 'bad';
  1791. $err = ($Matches[3] > 10) ? 10 : $Matches[3]; //max.
  1792. $this->accountTrack('Damaged sector count of ' . ($Matches[3]), $err);
  1793. }
  1794. return '<span class="log4">' . $Text . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1795. }
  1796. if (
  1797. strtolower($Matches[1]) == 'retry sector count'
  1798. || strtolower($Matches[1]) == 'jitter error (maybe fixed)'
  1799. || strtolower($Matches[1]) == 'edge jitter error (maybe fixed)'
  1800. || strtolower($Matches[1]) == 'atom jitter error (maybe fixed)'
  1801. || strtolower($Matches[1]) == 'drift error (maybe fixed)'
  1802. || strtolower($Matches[1]) == 'dropped bytes error (maybe fixed)'
  1803. || strtolower($Matches[1]) == 'duplicated bytes error (maybe fixed)'
  1804. ) {
  1805. if ($Matches[3] == 0) {
  1806. $Class = 'goodish';
  1807. } else {
  1808. $Class = 'badish';
  1809. }
  1810. return '<span class="log4">' . $Text . '</span> <span class="' . $Class . '">' . $Matches[3] . '</span>';
  1811. }
  1812. }
  1813. private function toc($Matches)
  1814. {
  1815. return "$Matches[1]<span class=\"log4\">$Matches[2]</span>$Matches[3]<strong>|</strong>" .
  1816. "$Matches[4]<span class=\"log1\">$Matches[5]</span>$Matches[7]<strong>|</strong>" .
  1817. "$Matches[8]<span class=\"log1\">$Matches[9]</span>$Matches[11]<strong>|</strong>" .
  1818. "$Matches[12]<span class=\"log1\">$Matches[13]</span>$Matches[14]<strong>|</strong>" .
  1819. "$Matches[15]<span class=\"log1\">$Matches[16]</span>$Matches[17]" . "\n";
  1820. }
  1821. private function checkTracks($LogArrayKey)
  1822. {
  1823. if (count($this->Tracks[$LogArrayKey]) === 0) { //no tracks
  1824. unset($this->Details);
  1825. if ($this->Combined) {
  1826. $this->Details[] = "Combined Log (" . $this->Combined . ")";
  1827. $this->Details[] = "Invalid log (" . $this->CurrLog . "), no tracks!";
  1828. } else {
  1829. $this->Details[] = "Invalid log, no tracks!";
  1830. }
  1831. $this->Score = 0;
  1832. }
  1833. }
  1834. private function account(
  1835. $Msg,
  1836. $Decrease = false,
  1837. $Score = false,
  1838. $InclCombined = false,
  1839. $Notice = false
  1840. ) {
  1841. $DecreaseScore = $SetScore = false;
  1842. $Append2 = '';
  1843. $Append1 = ($InclCombined) ? (($this->Combined) ? " (" . $this->CurrLog . ")" : '') : '';
  1844. $Prepend = ($Notice) ? '[Notice] ' : '';
  1845. if ($Decrease) {
  1846. $DecreaseScore = true;
  1847. $Append2 = ($Decrease > 0) ? ' (-' . $Decrease . ' point' . ($Decrease == 1 ? '' : 's') . ')' : '';
  1848. } elseif ($Score || $Score === 0) {
  1849. $SetScore = true;
  1850. $Decrease = 100 - $Score;
  1851. $Append2 = ($Decrease > 0) ? ' (-' . $Decrease . ' point' . ($Decrease == 1 ? '' : 's') . ')' : '';
  1852. }
  1853. if (!in_array($Prepend . $Msg . $Append1 . $Append2, $this->Details)) {
  1854. $this->Details[] = $Prepend . $Msg . $Append1 . $Append2;
  1855. if ($DecreaseScore) {
  1856. $this->Score -= $Decrease;
  1857. }
  1858. if ($SetScore) {
  1859. $this->Score = $Score;
  1860. }
  1861. }
  1862. }
  1863. private function accountTrack($Msg, $Decrease = false)
  1864. {
  1865. $tn = (intval($this->TrackNumber) < 10) ? '0' . intval($this->TrackNumber) : $this->TrackNumber;
  1866. $Append = '';
  1867. if ($Decrease) {
  1868. $this->DecreaseScoreTrack += $Decrease;
  1869. $Append = ' (-' . $Decrease . ' point' . ($Decrease == 1 ? '' : 's') . ')';
  1870. }
  1871. $Prepend = 'Track ' . $tn . (($this->Combined) ? " (" . $this->CurrLog . ")" : '') . ': ';
  1872. $this->BadTrack[] = $Prepend . $Msg . $Append;
  1873. }
  1874. public function getRipper()
  1875. {
  1876. return $this->ripper;
  1877. }
  1878. public function getRipperVersion()
  1879. {
  1880. return $this->ripperVersion;
  1881. }
  1882. public function getScore(): int
  1883. {
  1884. return $this->Score;
  1885. }
  1886. public function getDetails(): array
  1887. {
  1888. return $this->Details;
  1889. }
  1890. public function getChecksumState(): string
  1891. {
  1892. return $this->checksumStatus;
  1893. }
  1894. public function getLanguage(): string
  1895. {
  1896. return $this->language;
  1897. }
  1898. public static function getAcceptValues(): string
  1899. {
  1900. return ".txt,.TXT,.log,.LOG";
  1901. }
  1902. public static function getLogcheckerVersion(): string
  1903. {
  1904. $composer = json_decode(
  1905. file_get_contents(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'composer.json'])),
  1906. true
  1907. );
  1908. return $composer['version'];
  1909. }
  1910. }