No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

2084 líneas
84 KiB

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