using System; using System.IO; namespace psxt001z { internal class Track { #region Properties /// /// Original input path for the track /// private string InputPath { get; set; } /// /// Stream representing the track data /// private Stream InputStream { get; set; } /// /// Output for saving the track data /// private string OutputPath { get; set; } /// /// Starting offset within the file /// private int Start { get; set; } /// /// Size of the input file /// private int Size { get; set; } /// /// CRC-32 of the track data to compare against /// private uint CRC32 { get; set; } /// /// Audio data header /// private byte[] RiffData { get; set; } = new byte[44]; /// /// True if the track is audio data /// private bool IsRiff { get; set; } /// /// True if the file is under ~100MB /// private bool SmallFile { get; set; } = false; /// /// True to write out the track data /// private bool SaveTrack { get; set; } /// /// True means '+' or 'p' /// False means '-' or 'n' /// Null means neither /// private bool? Mode { get; set; } /// /// Cache for small file data /// private byte[] FileContents { get; set; } /// /// Cached file offset /// private int Offset { get; set; } = 0; /// /// Current file offset /// private int Current { get; set; } = 0; #endregion #region Constructor public Track(string filename, int start, int size, uint crc, bool isRiff = false, bool? mode = null, string output = null) { InputPath = filename; InputStream = File.OpenRead(filename); OutputPath = output; Start = start; Size = size; CRC32 = crc; Mode = mode; IsRiff = isRiff; SmallFile = Size <= 100_000_000; SaveTrack = output != null; Console.WriteLine($"File: {InputPath}\nStart: {Start}\nSize: {Size}\nCRC-32: {CRC32:8x}"); Console.WriteLine(); if (IsRiff) PopulateRiffData(); if (SmallFile) CacheFileData(); } #endregion #region Functions public bool FindTrack() { // Positive mode if (Mode == true) { if (Current > 20000) { Mode = null; return true; } Offset = Current; Current += 4; return MatchesCRC(); } // Negative mode else if (Mode == false) { if (Current > 20000) { Mode = null; return true; } Offset = -Current; Current += 4; return MatchesCRC(); } // Neutral mode else { if (Current > 20000) { Mode = null; return true; } Offset = Current; if (MatchesCRC()) { return true; } else { Offset = -Current; Current += 4; return MatchesCRC(); } } } public bool MatchesCRC() { CRC32 calc = new CRC32(); if (SmallFile) { if (IsRiff) calc.ProcessCRC(RiffData, 0, 44); calc.ProcessCRC(FileContents, (int)(20000 + Offset), (int)Size); } else { InputStream.Seek(Start + Offset, SeekOrigin.Begin); if (IsRiff) calc.ProcessCRC(RiffData, 0, 44); for (long i = 0; i < Size; i++) { byte[] buffer = new byte[1]; if (InputStream.Read(buffer, 0, 1) != 1) { buffer[0] = 0x00; InputStream.Seek(Start + Offset + i + 1, SeekOrigin.Begin); } calc.ProcessCRC(buffer, 0, 1); } } Console.Write($"Offset correction {Offset} bytes, {Offset / 4} samples, CRC-32 {calc.m_crc32:8x}"); return (calc.m_crc32 == CRC32); } public void Done() { if (SmallFile) FileContents = null; if (Mode == null) { Console.WriteLine(); Console.WriteLine("Can't find offset!"); return; } if (SaveTrack) { byte[] buffer = new byte[1]; Stream f2 = File.Open(OutputPath, FileMode.Create, FileAccess.ReadWrite); if (IsRiff) f2.Write(RiffData, 0, 44); InputStream.Seek(Start + Offset, SeekOrigin.Begin); for (long i = 0; i < Size; i++) { if (InputStream.Read(buffer, 0, 1) != 1) { buffer[0] = 0x00; InputStream.Seek(Start + Offset + i + 1, SeekOrigin.Begin); } f2.Write(buffer, 0, 1); } } Console.WriteLine(); Console.Write("DONE!"); Console.WriteLine(); Console.Write($"Offset correction: {Offset} bytes / {Offset / 4} samples"); } #endregion #region Utilities // TODO: Figure out what this actually does private void CacheFileData() { FileContents = new byte[Size + 40000]; InputStream.Seek(Start - 20000, SeekOrigin.Begin); for (int i = 0; i < Size + 40000; i++) { if (Start + i <= 20000) InputStream.Seek(Start + i - 20000, SeekOrigin.Begin); if (InputStream.Read(new byte[1], 0, 1) != 1) FileContents[i] = 0x00; } } private void PopulateRiffData() { RiffData[0] = 0x52; RiffData[1] = 0x49; RiffData[2] = 0x46; RiffData[3] = 0x46; byte[] temp = BitConverter.GetBytes(Size - 8); Array.Copy(temp, 0, RiffData, 4, 4); RiffData[8] = 0x57; RiffData[9] = 0x41; RiffData[10] = 0x56; RiffData[11] = 0x45; RiffData[12] = 0x66; RiffData[13] = 0x6D; RiffData[14] = 0x74; RiffData[15] = 0x20; RiffData[16] = 0x10; RiffData[17] = 0x00; RiffData[18] = 0x00; RiffData[19] = 0x00; RiffData[20] = 0x01; RiffData[21] = 0x00; RiffData[22] = 0x02; RiffData[23] = 0x00; RiffData[24] = 0x44; RiffData[25] = 0xAC; RiffData[26] = 0x00; RiffData[27] = 0x00; RiffData[28] = 0x10; RiffData[29] = 0xB1; RiffData[30] = 0x02; RiffData[31] = 0x00; RiffData[32] = 0x04; RiffData[33] = 0x00; RiffData[34] = 0x10; RiffData[35] = 0x00; RiffData[36] = 0x64; RiffData[37] = 0x61; RiffData[38] = 0x74; RiffData[39] = 0x61; temp = BitConverter.GetBytes(Size - 44); Array.Copy(temp, 0, RiffData, 40, 4); Size -= 44; } #endregion } }