// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : SubchannelLog.cs // Author(s) : Natalia Portillo // // Component : Core algorithms. // // --[ License ] -------------------------------------------------------------- // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ namespace Aaru.Core.Logging; using System; using System.IO; using Aaru.Decoders.CD; /// Logs subchannel data public class SubchannelLog { const int SUB_SIZE = 96; readonly bool _bcd; readonly StreamWriter _logSw; /// Initializes the subchannel log /// Output log file /// Drive returns subchannel in BCD format public SubchannelLog(string outputFile, bool bcd) { if(string.IsNullOrEmpty(outputFile)) return; _bcd = bcd; _logSw = new StreamWriter(outputFile, true); _logSw.WriteLine("Start subchannel logging at {0}", DateTime.Now); _logSw.WriteLine("######################################################"); _logSw.Flush(); } /// Finishes and closes the subchannel log public void Close() { _logSw.WriteLine("######################################################"); _logSw.WriteLine("End logging at {0}", DateTime.Now); _logSw.Close(); } /// Logs an entry to the subchannel log /// Subchannel data /// Set to true if the subchannel data is raw /// First LBA read from drive to retrieve the data /// Number of blocks read /// Set to true if the subchannel has been generated, false if read from media /// Set to true if the subchannel has been fixed, false if as is public void WriteEntry(byte[] subchannel, bool raw, long startingLba, uint blocks, bool generated, bool @fixed) { if(subchannel.Length / SUB_SIZE != blocks) { _logSw.WriteLine("Data length is invalid!"); _logSw.Flush(); return; } var p = new int[subchannel.Length / 8]; var q = new int[subchannel.Length / 8]; var r = new int[subchannel.Length / 8]; var s = new int[subchannel.Length / 8]; var t = new int[subchannel.Length / 8]; var u = new int[subchannel.Length / 8]; var v = new int[subchannel.Length / 8]; var w = new int[subchannel.Length / 8]; for(var i = 0; i < subchannel.Length; i += 8) { p[i / 8] = subchannel[i] & 0x80; p[i / 8] += (subchannel[i + 1] & 0x80) >> 1; p[i / 8] += (subchannel[i + 2] & 0x80) >> 2; p[i / 8] += (subchannel[i + 3] & 0x80) >> 3; p[i / 8] += (subchannel[i + 4] & 0x80) >> 4; p[i / 8] += (subchannel[i + 5] & 0x80) >> 5; p[i / 8] += (subchannel[i + 6] & 0x80) >> 6; p[i / 8] += (subchannel[i + 7] & 0x80) >> 7; q[i / 8] = (subchannel[i] & 0x40) << 1; q[i / 8] += subchannel[i + 1] & 0x40; q[i / 8] += (subchannel[i + 2] & 0x40) >> 1; q[i / 8] += (subchannel[i + 3] & 0x40) >> 2; q[i / 8] += (subchannel[i + 4] & 0x40) >> 3; q[i / 8] += (subchannel[i + 5] & 0x40) >> 4; q[i / 8] += (subchannel[i + 6] & 0x40) >> 5; q[i / 8] += (subchannel[i + 7] & 0x40) >> 6; r[i / 8] = (subchannel[i] & 0x20) << 2; r[i / 8] += (subchannel[i + 1] & 0x20) << 1; r[i / 8] += subchannel[i + 2] & 0x20; r[i / 8] += (subchannel[i + 3] & 0x20) >> 1; r[i / 8] += (subchannel[i + 4] & 0x20) >> 2; r[i / 8] += (subchannel[i + 5] & 0x20) >> 3; r[i / 8] += (subchannel[i + 6] & 0x20) >> 4; r[i / 8] += (subchannel[i + 7] & 0x20) >> 5; s[i / 8] = (subchannel[i] & 0x10) << 3; s[i / 8] += (subchannel[i + 1] & 0x10) << 2; s[i / 8] += (subchannel[i + 2] & 0x10) << 1; s[i / 8] += subchannel[i + 3] & 0x10; s[i / 8] += (subchannel[i + 4] & 0x10) >> 1; s[i / 8] += (subchannel[i + 5] & 0x10) >> 2; s[i / 8] += (subchannel[i + 6] & 0x10) >> 3; s[i / 8] += (subchannel[i + 7] & 0x10) >> 4; t[i / 8] = (subchannel[i] & 0x08) << 4; t[i / 8] += (subchannel[i + 1] & 0x08) << 3; t[i / 8] += (subchannel[i + 2] & 0x08) << 2; t[i / 8] += (subchannel[i + 3] & 0x08) << 1; t[i / 8] += subchannel[i + 4] & 0x08; t[i / 8] += (subchannel[i + 5] & 0x08) >> 1; t[i / 8] += (subchannel[i + 6] & 0x08) >> 2; t[i / 8] += (subchannel[i + 7] & 0x08) >> 3; u[i / 8] = (subchannel[i] & 0x04) << 5; u[i / 8] += (subchannel[i + 1] & 0x04) << 4; u[i / 8] += (subchannel[i + 2] & 0x04) << 3; u[i / 8] += (subchannel[i + 3] & 0x04) << 2; u[i / 8] += (subchannel[i + 4] & 0x04) << 1; u[i / 8] += subchannel[i + 5] & 0x04; u[i / 8] += (subchannel[i + 6] & 0x04) >> 1; u[i / 8] += (subchannel[i + 7] & 0x04) >> 2; v[i / 8] = (subchannel[i] & 0x02) << 6; v[i / 8] += (subchannel[i + 1] & 0x02) << 5; v[i / 8] += (subchannel[i + 2] & 0x02) << 4; v[i / 8] += (subchannel[i + 3] & 0x02) << 3; v[i / 8] += (subchannel[i + 4] & 0x02) << 2; v[i / 8] += (subchannel[i + 5] & 0x02) << 1; v[i / 8] += subchannel[i + 6] & 0x02; v[i / 8] += (subchannel[i + 7] & 0x02) >> 1; w[i / 8] = (subchannel[i] & 0x01) << 7; w[i / 8] += (subchannel[i + 1] & 0x01) << 6; w[i / 8] += (subchannel[i + 2] & 0x01) << 5; w[i / 8] += (subchannel[i + 3] & 0x01) << 4; w[i / 8] += (subchannel[i + 4] & 0x01) << 3; w[i / 8] += (subchannel[i + 5] & 0x01) << 2; w[i / 8] += (subchannel[i + 6] & 0x01) << 1; w[i / 8] += subchannel[i + 7] & 0x01; } for(uint block = 0; block < blocks; block++) { var rwEmpty = true; if(raw) for(uint i = 12 * block; i < 12 * block + 12; i++) { if(r[i] == 0 && s[i] == 0 && t[i] == 0 && u[i] == 0 && v[i] == 0 && w[i] == 0 || r[i] == 0xFF && s[i] == 0xFF && t[i] == 0xFF && u[i] == 0xFF && v[i] == 0xFF && w[i] == 0xFF) continue; rwEmpty = false; break; } var corruptedPause = false; var pause = false; for(var i = 0; i < 12; i++) { if(p[i] == 0 || p[i] == 0xFF) continue; corruptedPause = true; break; } if(!corruptedPause) pause = p[0] == 1; var subBuf = new byte[12]; subBuf[0] = (byte)q[0 + block * 12]; subBuf[1] = (byte)q[1 + block * 12]; subBuf[2] = (byte)q[2 + block * 12]; subBuf[3] = (byte)q[3 + block * 12]; subBuf[4] = (byte)q[4 + block * 12]; subBuf[5] = (byte)q[5 + block * 12]; subBuf[6] = (byte)q[6 + block * 12]; subBuf[7] = (byte)q[7 + block * 12]; subBuf[8] = (byte)q[8 + block * 12]; subBuf[9] = (byte)q[9 + block * 12]; subBuf[10] = (byte)q[10 + block * 12]; subBuf[11] = (byte)q[11 + block * 12]; string prettyQ = Subchannel.PrettifyQ(subBuf, generated || _bcd, startingLba + block, corruptedPause, pause, rwEmpty); if(generated) prettyQ += " (GENERATED)"; else if(@fixed) prettyQ += " (FIXED)"; _logSw.WriteLine(prettyQ); } _logSw.Flush(); } /// Logs message indicating the P subchannel has been fixed /// LBA fix belongs to public void WritePFix(long lba) => WriteMessageWithPosition(lba, "fixed P subchannel using weight average."); /// Logs message indicating the R-W subchannels have been fixed /// LBA fix belongs to public void WriteRwFix(long lba) => WriteMessageWithPosition(lba, "fixed R-W subchannels writing empty data."); /// Logs message indicating the ADR field of the Q subchannel has been fixed /// LBA fix belongs to public void WriteQAdrFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct ADR."); /// Logs message indicating the CONTROL field of the Q subchannel has been fixed /// LBA fix belongs to public void WriteQCtrlFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct CONTROL."); /// Logs message indicating the ZERO field of the Q subchannel has been fixed /// LBA fix belongs to public void WriteQZeroFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct ZERO."); /// Logs message indicating the TNO field of the Q subchannel has been fixed /// LBA fix belongs to public void WriteQTnoFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct TNO."); /// Logs message indicating the INDEX field of the Q subchannel has been fixed /// LBA fix belongs to public void WriteQIndexFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct INDEX."); /// Logs message indicating the relative position of the Q subchannel has been fixed /// LBA fix belongs to public void WriteQRelPosFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct RELATIVE POSITION."); /// Logs message indicating the absolute position of the Q subchannel has been fixed /// LBA fix belongs to public void WriteQAbsPosFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct ABSOLUTE POSITION."); /// Logs message indicating the CRC of the Q subchannel has been fixed /// LBA fix belongs to public void WriteQCrcFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct CRC."); /// Logs message indicating the the Q subchannel has been fixed with a known good MCN /// LBA fix belongs to public void WriteQMcnFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with known good MCN."); /// Logs message indicating the the Q subchannel has been fixed with a known good ISRC /// LBA fix belongs to public void WriteQIsrcFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with known good ISRC."); /// Logs a message with a specified position /// LBA position /// Message to log public void WriteMessageWithPosition(long lba, string message) { long minute = (lba + 150) / 4500; long second = (lba + 150) % 4500 / 75; long frame = (lba + 150) % 4500 % 75; string area = lba < 0 ? "Lead-In" : "Program"; _logSw.WriteLine($"{minute:D2}:{second:D2}:{frame:D2} - LBA {lba,6}: {area} area, {message}"); _logSw.Flush(); } }