diff --git a/BurnOutSharp/External/README.MD b/BurnOutSharp/External/README.MD index fe4c2864..eaff1d1a 100644 --- a/BurnOutSharp/External/README.MD +++ b/BurnOutSharp/External/README.MD @@ -6,6 +6,6 @@ This directory contains multiple external libraries. Here is the status of each: | --------- | ------- | ------ | | libgsf | [libgsf](https://github.com/GNOME/libgsf) | 90% ported to C#, currently unused | | libmsi | [msitools](https://github.com/GNOME/msitools) | 100% ported to C#, breaking bugs, currently unused | -| psxt001z | [psxt001z](https://github.com/Dremora/psxt001z) | Unknown amount ported to C# | +| psxt001z | [psxt001z](https://github.com/Dremora/psxt001z) | 90% ported to C#, device reading disabled | | SevenZip | [7-Zip (ComHandler.cpp)](https://github.com/mcmilk/7-Zip/blob/63128687a9dffbe60091d04df3baf38af60dc2fa/CPP/7zip/Archive/ComHandler.cpp) | 25% ported to C#, currently unused | | stormlibsharp | [stormlibsharp](https://github.com/robpaveza/stormlibsharp) | External submodule | \ No newline at end of file diff --git a/BurnOutSharp/External/psxt001z/CRC16.cs b/BurnOutSharp/External/psxt001z/CRC16.cs index ef3c45c1..ed146ade 100644 --- a/BurnOutSharp/External/psxt001z/CRC16.cs +++ b/BurnOutSharp/External/psxt001z/CRC16.cs @@ -1,9 +1,9 @@ -namespace BurnOutSharp.External.psxt001z +namespace psxt001z { public class CRC16 { // Table of CRC constants - implements x^16+x^12+x^5+1 - private static ushort[] crc16_tab = new ushort[] + private static ushort[] CRC16Table = new ushort[] { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, @@ -44,7 +44,7 @@ ushort cksum = 0; for (int i = 0; i < len; i++) { - cksum = (ushort)(crc16_tab[((cksum >> 8) ^ buf[bufPtr++]) & 0xFF] ^ (cksum << 8)); + cksum = (ushort)(CRC16Table[((cksum >> 8) ^ buf[bufPtr++]) & 0xFF] ^ (cksum << 8)); } return (ushort)(~cksum); diff --git a/BurnOutSharp/External/psxt001z/CRC32.cs b/BurnOutSharp/External/psxt001z/CRC32.cs new file mode 100644 index 00000000..cf3830e3 --- /dev/null +++ b/BurnOutSharp/External/psxt001z/CRC32.cs @@ -0,0 +1,57 @@ +namespace psxt001z +{ + internal class CRC32 + { + #region Constants + + private const uint CRC_POLY = 0xEDB88320; + + private const uint CRC_MASK = 0xD202EF8D; + + #endregion + + #region Properties + + protected uint[] Table { get; private set; } = new uint[256]; + + public uint m_crc32 { get; set; } + + #endregion + + #region Constructor + + public CRC32() + { + for (uint i = 0; i < 256; i++) + { + uint r, j; + for (r = i, j = 8; j != 0; j--) + { + r = ((r & 1) != 0) ? (r >> 1) ^ CRC_POLY : r >> 1; + } + + Table[i] = r; + } + + m_crc32 = 0; + } + + #endregion + + #region Functions + + public void ProcessCRC(byte[] pData, int pDataPtr, int nLen) + { + uint crc = m_crc32; + while (nLen-- != 0) + { + crc = Table[(byte)(crc ^ pData[pDataPtr++])] ^ crc >> 8; + crc ^= CRC_MASK; + } + + m_crc32 = crc; + } + + #endregion + } +} diff --git a/BurnOutSharp/External/psxt001z/Common.cs b/BurnOutSharp/External/psxt001z/Common.cs new file mode 100644 index 00000000..7c2fa434 --- /dev/null +++ b/BurnOutSharp/External/psxt001z/Common.cs @@ -0,0 +1,19 @@ +namespace psxt001z +{ + public static class Common + { + public const int ZERO = 0; + + public const string VERSION = "v0.21 beta 1"; + + /// + /// BCD to u_char + /// + public static byte btoi(byte b) => (byte)(((b) / 16 * 10 + (b) % 16)); + + /// + /// u_char to BCD + /// + public static byte itob(byte i) => (byte)(((i) / 10 * 16 + (i) % 10)); + } +} diff --git a/BurnOutSharp/External/psxt001z/FileTools.cs b/BurnOutSharp/External/psxt001z/FileTools.cs new file mode 100644 index 00000000..82b30fc4 --- /dev/null +++ b/BurnOutSharp/External/psxt001z/FileTools.cs @@ -0,0 +1,178 @@ +using System; +using System.IO; +using System.Text; + +namespace psxt001z +{ + internal class FileTools + { + #region Properties + + private Stream InputStream { get; set; } + + private byte[] ExeName { get; set; } = new byte[20]; + + private byte[] DateValue { get; set; } = new byte[11]; + + #endregion + + #region Constructor + + public FileTools(Stream file) + { + InputStream = file; + } + + #endregion + + #region Functions + + /// + /// Get file size + /// + public long size() => InputStream.Length; + + /// + /// Get executable name + /// + public string exe() + { + InputStream.Seek(51744, SeekOrigin.Begin); + + string filename = string.Empty; + while (filename != "SYSTEM.CNF") + { + byte[] buf = new byte[10]; + InputStream.Read(buf, 0, 10); + filename = Encoding.ASCII.GetString(buf); + InputStream.Seek(-9, SeekOrigin.Current); + } + + byte[] buffer = new byte[20]; + + InputStream.Seek(-32, SeekOrigin.Current); + InputStream.Read(buffer, 0, 4); + uint lba = BitConverter.ToUInt32(buffer, 0); + + InputStream.Seek((2352 * lba) + 29, SeekOrigin.Begin); + InputStream.Read(buffer, 0, 6); + + string iniLine = Encoding.ASCII.GetString(buffer); + while (iniLine != "cdrom:") + { + InputStream.Seek(-5, SeekOrigin.Current); + InputStream.Read(buffer, 0, 6); + iniLine = Encoding.ASCII.GetString(buffer); + } + + InputStream.Read(buffer, 0, 1); + if (buffer[0] != '\\') + InputStream.Seek(-1, SeekOrigin.Current); + + int i = -1; + do + { + InputStream.Read(buffer, ++i, 1); + } while (buffer[i] != ';'); + + for (long a = 0; a < i; a++) + { + ExeName[a] = (byte)char.ToUpper((char)buffer[a]); + } + + return Encoding.ASCII.GetString(ExeName); + } + + /// + /// Get human-readable date + /// + public string date() + { + byte[] buffer = new byte[12], datenofrmt = new byte[3]; + + InputStream.Seek(51744, SeekOrigin.Begin); + + do + { + InputStream.Read(buffer, 0, 11); + buffer[11] = 0; + InputStream.Seek(-10, SeekOrigin.Current); + } while (Encoding.ASCII.GetString(ExeName) != Encoding.ASCII.GetString(buffer)); + + InputStream.Seek(-16, SeekOrigin.Current); + InputStream.Read(datenofrmt, 0, 3); + + if (datenofrmt[0] < 50) + { + byte[] year = Encoding.ASCII.GetBytes($"{2000 + datenofrmt[0]}"); + Array.Copy(year, 0, buffer, 0, 4); + } + else + { + byte[] year = Encoding.ASCII.GetBytes($"{1900 + datenofrmt[0]}"); + Array.Copy(year, 0, buffer, 0, 4); + } + + DateValue[4] = (byte)'-'; + if (datenofrmt[1] < 10) + { + byte[] month = Encoding.ASCII.GetBytes($"0{datenofrmt[1]}"); + Array.Copy(month, 0, buffer, 5, 2); + } + else + { + byte[] month = Encoding.ASCII.GetBytes($"{datenofrmt[1]}"); + Array.Copy(month, 0, buffer, 5, 2); + } + + DateValue[7] = (byte)'-'; + if (datenofrmt[2] < 10) + { + byte[] day = Encoding.ASCII.GetBytes($"0{datenofrmt[2]}"); + Array.Copy(day, 0, buffer, 8, 2); + } + else + { + byte[] day = Encoding.ASCII.GetBytes($"{datenofrmt[2]}"); + Array.Copy(day, 0, buffer, 8, 2); + } + + return Encoding.ASCII.GetString(DateValue); + } + + /// + /// Resize the image + /// + public int resize(long newsize) + { + long oldsize = size(); + if (oldsize < newsize) + { + InputStream.SetLength(newsize); + return 1; + } + else if (oldsize > newsize) + { + InputStream.SetLength(newsize); + return 2; + } + else + { + return 0; + } + } + + /// + /// Get the reported sector count from the image + /// + public int imagesize() + { + InputStream.Seek(0x9368, SeekOrigin.Begin); + byte[] sizebuf = new byte[4]; + InputStream.Read(sizebuf, 0, 4); + return BitConverter.ToInt32(sizebuf, 0); + } + + #endregion + } +} diff --git a/BurnOutSharp/External/psxt001z/Functions.cs b/BurnOutSharp/External/psxt001z/Functions.cs new file mode 100644 index 00000000..ddf4b012 --- /dev/null +++ b/BurnOutSharp/External/psxt001z/Functions.cs @@ -0,0 +1,108 @@ +using System; +using System.IO; +using System.Text; +using static psxt001z.Common; + +namespace psxt001z +{ + internal static class Functions + { + public static int CalculateEDC(in byte[] src, int srcPtr, int size, int[] edc_lut) + { + int edc = 0; + while (size-- != 0) + { + edc = (edc >> 8) ^ edc_lut[(edc ^ src[srcPtr++]) & 0xFF]; + } + + return edc; + } + + public static bool ZeroCompare(byte[] buffer, int bufferPtr, int bsize) + { + for (int i = 0; i < bsize; i++) + { + if (buffer[bufferPtr + i] != 0x00) + return false; + } + + return true; + } + + public static void MSF(long lba, byte[] buffer, int bufferOffset) + { + lba += 150; + + double mindbl = lba / 60 / 75; + byte min = (byte)Math.Floor(mindbl); + double secdbl = (lba - (min * 60 * 75)) / 75; + byte sec = (byte)Math.Floor(secdbl); + byte frame = (byte)(lba - (min * 60 * 75) - (sec * 75)); + + buffer[bufferOffset] = itob(min); + buffer[bufferOffset + 1] = itob(sec); + buffer[bufferOffset + 2] = itob(frame); + + return; + } + + public static bool GetEDCStatus(Stream file) + { + long currentposition = file.Position; + + file.Seek(30572, SeekOrigin.Begin); + + byte[] buffer = new byte[4]; + file.Read(buffer, 0, 4); + + file.Seek(currentposition, SeekOrigin.Begin); + + return BitConverter.ToInt32(buffer, 0) == 0; + } + + public static byte[] ExecutableName(Stream file) + { + byte[] buffer = new byte[20]; + byte[] exename = new byte[20]; + + //Searching for SYSTEM.CNF + file.Seek(51744, SeekOrigin.Begin); + while (Encoding.ASCII.GetString(buffer) != "SYSTEM.CNF") + { + file.Read(buffer, 0, 10); + buffer[10] = 0; + file.Seek(-9, SeekOrigin.Current); + } + + file.Seek(-32, SeekOrigin.Current); + byte[] lba = new byte[4]; + file.Read(lba, 0, 4); + file.Seek((2352 * BitConverter.ToInt32(lba, 0)) + 29, SeekOrigin.Begin); + file.Read(buffer, 0, 6); + buffer[6] = 0; + while (Encoding.ASCII.GetString(buffer) != "cdrom:") + { + file.Seek(-5, SeekOrigin.Current); + file.Read(buffer, 0, 6); + } + + file.Read(buffer, 0, 1); + if (buffer[0] != '\\') + file.Seek(-1, SeekOrigin.Current); + + int i = -1; + do + { + file.Read(buffer, ++i, 1); + } while (buffer[i] != ';'); + + for (int a = 0; a < i; a++) + { + exename[a] = (byte)char.ToUpper((char)buffer[a]); + } + + exename[i] = 0; + return exename; + } + } +} diff --git a/BurnOutSharp/External/psxt001z/Info.cs b/BurnOutSharp/External/psxt001z/Info.cs new file mode 100644 index 00000000..84af29c1 --- /dev/null +++ b/BurnOutSharp/External/psxt001z/Info.cs @@ -0,0 +1,293 @@ +using System; +using System.IO; +using System.Linq; +using static psxt001z.Functions; + +namespace psxt001z +{ + public class Info + { + #region Constants + + private static readonly byte[] edc_form_2 = { 0x3F, 0x13, 0xB0, 0xBE }; + + private static readonly byte[] syncheader = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + private static readonly byte[] subheader = { 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00 }; + + #endregion + + #region Functions + + public static int GetInfo(string filename, bool fix) + { + // Variables + bool errors = false; + byte[] buffer = new byte[2352], buffer2 = new byte[2352]; + int mode = 15; // synñheader[15]; + + #region Opening image + + Stream image; + try + { + FileAccess open_mode = fix ? FileAccess.ReadWrite : FileAccess.Read; + image = File.Open(filename, FileMode.Open, open_mode); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + return 1; + } + + long size = image.Length; + Console.WriteLine($"File: {filename}"); + + #endregion + + #region Type + + image.Read(buffer, 0, 12); + + int sectorsize; + if (buffer.Take(12).SequenceEqual(syncheader.Take(12))) + { + sectorsize = 2352; + } + else + { + sectorsize = 2048; + } + if (size % sectorsize != 0) + { + Console.WriteLine($"{filename}: not ModeX/{sectorsize} image!"); + return 1; + } + + long sectors = size / sectorsize; + + #endregion + + #region Mode + + if (sectorsize == 2352) + { + image.Seek(0xF, SeekOrigin.Begin); + mode = image.ReadByte(); + if (mode != 1 && mode != 2) + { + Console.WriteLine($"{filename}: unknown mode!"); + return 1; + } + } + else + { + mode = -1; + } + + #endregion + + #region Size + + image.Seek(sectorsize * 16 + ((mode == 2) ? 24 : ((mode == 1) ? 16 : 0)) + 0x50, SeekOrigin.Begin); + + // ISO size + byte[] buf = new byte[4]; + image.Read(buf, 0, 4); + int realsectors = BitConverter.ToInt32(buf, 0); + + image.Seek(0, SeekOrigin.Begin); + int realsize = realsectors * sectorsize; + if (sectors == realsectors) + { + Console.WriteLine($"Size (bytes): {size} (OK)"); + Console.WriteLine($"Size (sectors): {sectors} (OK)"); + } + else + { + Console.WriteLine($"Size (bytes): {size}"); + Console.WriteLine($"From image: {realsize}"); + Console.WriteLine($"Size (sectors): {sectors}"); + Console.WriteLine($"From image: {realsectors}"); + } + + #endregion + + #region Mode + + if (mode > 0) + Console.WriteLine($"Mode: {mode}"); + + if (mode == 2) + { + #region EDC in Form 2 + + bool imageedc = GetEDCStatus(image); + Console.WriteLine($"EDC in Form 2 sectors: {(imageedc ? "YES" : "NO")}"); + + #endregion + + #region Sysarea + + string systemArea = "System area: "; + image.Seek(0, SeekOrigin.Begin); + + CRC32 crc = new CRC32(); + for (int i = 0; i < 16; i++) + { + image.Read(buffer, 0, 2352); + crc.ProcessCRC(buffer, 0, 2352); + } + + uint imagecrc = crc.m_crc32; + systemArea += GetEdcType(imagecrc); + + Console.WriteLine(systemArea); + + #endregion + + #region Postgap + + image.Seek((sectors - 150) * sectorsize + 16, SeekOrigin.Begin); + image.Read(buffer, 0, 2336); + + string postgap = "Postgap type: Form "; + if ((buffer[2] >> 5 & 0x1) != 0) + { + postgap += "2"; + if (buffer.Take(8).SequenceEqual(subheader)) + postgap += ", zero subheader"; + else + postgap += ", non-zero subheader"; + + if (ZeroCompare(buffer, 8, 2324)) + postgap += ", zero data"; + else + postgap += ", non-zero data"; + + if (ZeroCompare(buffer, 2332, 4)) + postgap += ", no EDC"; + else + postgap += ", EDC"; + } + else + { + postgap += "1"; + if (ZeroCompare(buffer, 0, 8)) + postgap += ", zero subheader"; + else + postgap += ", non-zero subheader"; + + if (ZeroCompare(buffer, 8, 2328)) + postgap += ", zero data"; + else + postgap += ", non-zero data"; + } + + Console.WriteLine(postgap); + Array.Copy(buffer, buffer2, 2336); + + #endregion + } + + if (mode < 0) + return 0; + + for (long sector = sectors - 150; sector < sectors; sector++) + { + bool bad = false; + image.Seek(sector * sectorsize, SeekOrigin.Begin); + image.Read(buffer, 0, sectorsize); + + // Sync + string sectorInfo = string.Empty; + + MSF(sector, syncheader, 12); + if (!syncheader.SequenceEqual(buffer.Take(16))) + { + sectorInfo += $"Sector {sector}: Sync/Header"; + bad = true; + if (fix) + { + image.Seek(sector * sectorsize, SeekOrigin.Begin); + image.Write(syncheader, 0, 16); + sectorInfo += (" (fixed)"); + } + } + + // Mode 2 + if (mode == 2 && buffer.Skip(16).Take(2336).SequenceEqual(buffer2)) + { + if (bad) + { + sectorInfo += ", Subheader/Data/EDC/ECC"; + } + else + { + sectorInfo = $"Sector {sector}: Subheader/Data/EDC/ECC"; + bad = true; + } + + if (fix) + { + image.Seek(sector * sectorsize + 16, SeekOrigin.Begin); + image.Write(buffer2, 0, 2336); + sectorInfo += " (fixed)"; + } + } + + Console.WriteLine(sectorInfo); + + if (bad && (sector + 1 != sectors)) + errors = true; + } + + if (errors) + { + Console.WriteLine("NOTICE: One or more errors were found not in the last sector."); + Console.WriteLine("Please mention this when submitting dump info."); + } + else + { + Console.WriteLine("Done."); + } + + #endregion + + return 0; + } + + #endregion + + #region Utilities + + internal static string GetEdcType(uint imageCrc) + { + switch (imageCrc) + { + case 0x11e3052d: + return "Eu EDC"; + case 0x808c19f6: + return "Eu NoEDC"; + case 0x70ffa73e: + return "Eu Alt NoEDC"; + case 0x7f9a25b1: + return "Eu Alt 2 EDC"; + case 0x783aca30: + return "Jap EDC"; + case 0xe955d6eb: + return "Jap NoEDC"; + case 0x9b519a2e: + return "US EDC"; + case 0x0a3e86f5: + return "US NoEDC"; + case 0x6773d4db: + return "US Alt NoEDC"; + default: + return $"Unknown, crc {imageCrc:8x}"; + } + } + + #endregion + } +} diff --git a/BurnOutSharp/External/psxt001z/LibCrypt.cs b/BurnOutSharp/External/psxt001z/LibCrypt.cs index 0f9539a6..cda89e67 100644 --- a/BurnOutSharp/External/psxt001z/LibCrypt.cs +++ b/BurnOutSharp/External/psxt001z/LibCrypt.cs @@ -1,16 +1,44 @@ using System; using System.IO; using System.Linq; +using System.Text; +using static psxt001z.Common; -namespace BurnOutSharp.External.psxt001z +namespace psxt001z { - /// - /// LibCrypt detection code - /// Originally written by Dremora: https://github.com/Dremora/psxt001z - /// Ported and changed by darksabre76 - /// + internal class ScsiPassThroughDirect + { + public ushort Length { get; set; } + + public byte ScsiStatus { get; set; } + + public byte PathId { get; set; } + + public byte TargetId { get; set; } + + public byte Lun { get; set; } + + public byte CDBLength { get; set; } + + public byte SenseInfoLength { get; set; } + + public byte DataIn { get; set; } + + public uint DataTransferLength { get; set; } + + public uint TimeOutValue { get; set; } + + public byte[] DataBuffer { get; set; } + + public uint SenseInfoOffset { get; set; } + + public byte[] CDB { get; set; } = new byte[16]; + } + public class LibCrypt { + #region OLD + public static bool CheckSubfile(string subFilePath) { // Check the file exists first @@ -113,16 +141,685 @@ namespace BurnOutSharp.External.psxt001z return modifiedSectors != 0; } - private static byte btoi(byte b) + #endregion + + #region Constants + + private const uint IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x4D014; + private const byte SCSI_IOCTL_DATA_IN = 0x1; + private const byte RAW_READ_CMD = 0xBE; + private const int BUFFER_LEN = 96; + private const int SENSE_SIZE = 0; //14 + private const string F_NAME = "sectors.log"; + private const int CYCLES = 5; + private const int LIBCRYPT_NUM_SECTORS = 64; + private const int READ_TIMES = 5; + + private static readonly uint[] lc_addresses = new uint[LIBCRYPT_NUM_SECTORS] +{ + 13955, 13960, 14081, 14086, 14335, 14340, 14429, 14434, + 14499, 14504, 14749, 14754, 14906, 14911, 14980, 14985, + 15092, 15097, 15162, 15167, 15228, 15233, 15478, 15483, + 15769, 15774, 15881, 15886, 15951, 15956, 16017, 16022, + 41895, 41900, 42016, 42021, 42282, 42287, 42430, 42435, + 42521, 42526, 42663, 42668, 42862, 42867, 43027, 43032, + 43139, 43144, 43204, 43209, 43258, 43263, 43484, 43489, + 43813, 43818, 43904, 43909, 44009, 44014, 44162, 44167 +}; + + private static readonly byte[] lc1_sectors_contents = new byte[768] { - /* BCD to u_char */ - return (byte)((b) / 16 * 10 + (b) % 16); + 0x41, 0x01, 0x01, 0x07, 0x06, 0x05, 0x00, 0x23, 0x08, 0x05, 0x38, 0x39, + 0x41, 0x01, 0x01, 0x03, 0x06, 0x11, 0x00, 0x03, 0x08, 0x90, 0x5d, 0xa0, + 0x41, 0x01, 0x01, 0x07, 0x07, 0x56, 0x00, 0x23, 0x09, 0x56, 0xdf, 0xde, + 0x41, 0x01, 0x01, 0x03, 0x07, 0x60, 0x00, 0x03, 0x09, 0xe1, 0xf2, 0x50, + 0x41, 0x01, 0x01, 0x03, 0x13, 0x10, 0x00, 0x03, 0x53, 0x10, 0x50, 0xec, + 0x41, 0x01, 0x01, 0x43, 0x11, 0x15, 0x00, 0x01, 0x13, 0x15, 0x23, 0x1e, + 0x41, 0x01, 0x01, 0x03, 0x12, 0x09, 0x00, 0x03, 0x14, 0x2d, 0x04, 0x73, + 0x41, 0x01, 0x01, 0x03, 0x1a, 0x34, 0x00, 0x03, 0x04, 0x34, 0xe2, 0xcf, + 0x41, 0x01, 0x01, 0x03, 0x13, 0x20, 0x00, 0x03, 0x15, 0x04, 0x82, 0x35, + 0x41, 0x01, 0x01, 0x01, 0x13, 0x29, 0x00, 0x43, 0x15, 0x29, 0x72, 0xe2, + 0x41, 0x01, 0x01, 0x03, 0x1e, 0x49, 0x00, 0x03, 0x08, 0x49, 0x32, 0xc5, + 0x41, 0x01, 0x01, 0x01, 0x16, 0x54, 0x00, 0x43, 0x18, 0x54, 0xd4, 0x79, + 0x41, 0x01, 0x01, 0x03, 0x18, 0x57, 0x00, 0x03, 0x20, 0xd6, 0xbc, 0x27, + 0x41, 0x01, 0x01, 0x03, 0x38, 0x61, 0x00, 0x03, 0x24, 0x61, 0x91, 0xa9, + 0x41, 0x01, 0x01, 0x0b, 0x19, 0x55, 0x00, 0x13, 0x21, 0x55, 0x14, 0x07, + 0x41, 0x01, 0x01, 0x03, 0x19, 0x62, 0x00, 0x03, 0x21, 0x20, 0x5d, 0x48, + 0x41, 0x01, 0x01, 0x03, 0x23, 0x17, 0x00, 0x03, 0x63, 0x17, 0x6d, 0xc6, + 0x41, 0x01, 0x01, 0x43, 0x21, 0x22, 0x00, 0x01, 0x23, 0x22, 0x24, 0x89, + 0x41, 0x01, 0x01, 0x03, 0x02, 0x12, 0x00, 0x03, 0x20, 0x12, 0x49, 0x43, + 0x41, 0x01, 0x01, 0x03, 0x22, 0x07, 0x00, 0x03, 0x24, 0x1f, 0x3a, 0xb1, + 0x41, 0x01, 0x01, 0x03, 0x23, 0x13, 0x00, 0x03, 0x25, 0x0b, 0x93, 0xc9, + 0x41, 0x01, 0x01, 0x0b, 0x23, 0x08, 0x00, 0x13, 0x25, 0x08, 0xce, 0x5d, + 0x41, 0x01, 0x01, 0x03, 0x06, 0x28, 0x00, 0x03, 0x2c, 0x28, 0xd7, 0xd6, + 0x41, 0x01, 0x01, 0x0b, 0x26, 0x33, 0x00, 0x13, 0x28, 0x33, 0x9c, 0x29, + 0x41, 0x01, 0x01, 0x03, 0x30, 0x59, 0x00, 0x03, 0x32, 0x1b, 0x2c, 0xc6, + 0x41, 0x01, 0x01, 0x03, 0x20, 0x24, 0x00, 0x03, 0x3a, 0x24, 0xe6, 0xac, + 0x41, 0x01, 0x01, 0x13, 0x31, 0x56, 0x00, 0x0b, 0x33, 0x56, 0x97, 0xed, + 0x41, 0x01, 0x01, 0x03, 0x31, 0x65, 0x00, 0x03, 0x33, 0x41, 0xba, 0x63, + 0x41, 0x01, 0x01, 0x01, 0x32, 0x51, 0x00, 0x43, 0x34, 0x51, 0xd7, 0xa9, + 0x41, 0x01, 0x01, 0x03, 0x33, 0x56, 0x00, 0x03, 0xb4, 0x56, 0xc0, 0x9a, + 0x41, 0x01, 0x01, 0x03, 0x32, 0x42, 0x00, 0x03, 0xb5, 0x42, 0x69, 0xe2, + 0x41, 0x01, 0x01, 0x03, 0x33, 0x07, 0x00, 0x03, 0x35, 0x45, 0x1a, 0x10, + 0x41, 0x01, 0x01, 0x09, 0x18, 0x65, 0x00, 0x09, 0x20, 0x41, 0x40, 0x72, + 0x41, 0x01, 0x01, 0x19, 0x18, 0x50, 0x00, 0x01, 0x20, 0x50, 0x25, 0xeb, + 0x41, 0x01, 0x01, 0x08, 0x20, 0x16, 0x00, 0x89, 0x22, 0x16, 0x95, 0xa8, + 0x41, 0x01, 0x01, 0x09, 0x20, 0x01, 0x00, 0x09, 0x22, 0x25, 0xb8, 0x26, + 0x41, 0x01, 0x01, 0x09, 0x23, 0x53, 0x00, 0x09, 0x25, 0x77, 0x21, 0x03, + 0x41, 0x01, 0x01, 0x0b, 0x23, 0x62, 0x00, 0x49, 0x25, 0x62, 0x68, 0x4c, + 0x41, 0x01, 0x01, 0x0d, 0x25, 0x55, 0x00, 0x29, 0x27, 0x55, 0xae, 0x41, + 0x41, 0x01, 0x01, 0x09, 0x25, 0x61, 0x00, 0x09, 0x27, 0xe0, 0xe7, 0x0e, + 0x41, 0x01, 0x01, 0x08, 0x26, 0x71, 0x00, 0x89, 0x28, 0x71, 0x95, 0xcb, + 0x41, 0x01, 0x01, 0x09, 0x27, 0x21, 0x00, 0x09, 0x29, 0x05, 0x80, 0x4b, + 0x41, 0x01, 0x01, 0x0b, 0x28, 0x63, 0x00, 0x49, 0x30, 0x63, 0xed, 0x18, + 0x41, 0x01, 0x01, 0x09, 0x29, 0x68, 0x00, 0x09, 0xb0, 0x68, 0xb0, 0x8c, + 0x41, 0x01, 0x01, 0x29, 0x31, 0x37, 0x00, 0x0d, 0x33, 0x37, 0x6c, 0x68, + 0x41, 0x01, 0x01, 0x09, 0x31, 0x4a, 0x00, 0x09, 0x33, 0x52, 0x7c, 0x8b, + 0x41, 0x01, 0x01, 0x09, 0x73, 0x52, 0x00, 0x09, 0x37, 0x52, 0x4b, 0x06, + 0x41, 0x01, 0x01, 0x19, 0x33, 0x57, 0x00, 0x01, 0x35, 0x57, 0x38, 0xf4, + 0x41, 0x01, 0x01, 0x09, 0x35, 0x04, 0x00, 0x09, 0x37, 0x1c, 0x54, 0x6a, + 0x41, 0x01, 0x01, 0x09, 0x31, 0x19, 0x00, 0x09, 0x17, 0x19, 0xa4, 0xbd, + 0x41, 0x01, 0x01, 0x01, 0x36, 0x04, 0x00, 0x19, 0x38, 0x04, 0x9c, 0xdf, + 0x41, 0x01, 0x01, 0x09, 0x36, 0x0b, 0x00, 0x09, 0x38, 0x49, 0x6c, 0x08, + 0x41, 0x01, 0x01, 0x49, 0x36, 0x58, 0x00, 0x0b, 0x38, 0x58, 0x99, 0xbf, + 0x41, 0x01, 0x01, 0x09, 0x36, 0x73, 0x00, 0x09, 0x38, 0x6b, 0xfe, 0x96, + 0x41, 0x01, 0x01, 0x0b, 0x39, 0x59, 0x00, 0x49, 0x41, 0x59, 0x54, 0x0d, + 0x41, 0x01, 0x01, 0x09, 0x39, 0x24, 0x00, 0x09, 0x41, 0x66, 0x9e, 0x67, + 0x41, 0x01, 0x01, 0x09, 0x44, 0x1b, 0x00, 0x09, 0x46, 0x03, 0x78, 0x0d, + 0x41, 0x01, 0x01, 0x09, 0x46, 0x18, 0x00, 0x09, 0x06, 0x18, 0x25, 0x99, + 0x41, 0x01, 0x01, 0x09, 0x45, 0x2b, 0x00, 0x09, 0x47, 0x69, 0xd3, 0xc5, + 0x41, 0x01, 0x01, 0x09, 0x05, 0x34, 0x00, 0x09, 0x45, 0x34, 0x35, 0x79, + 0x41, 0x01, 0x01, 0x09, 0x44, 0x59, 0x00, 0x09, 0x08, 0x59, 0x6e, 0x0a, + 0x41, 0x01, 0x01, 0x49, 0x46, 0x64, 0x00, 0x0b, 0x48, 0x64, 0xa4, 0x60, + 0x41, 0x01, 0x01, 0x09, 0x08, 0x62, 0x00, 0x09, 0x52, 0x62, 0x03, 0x5a, + 0x41, 0x01, 0x01, 0x19, 0x48, 0x67, 0x00, 0x01, 0x50, 0x67, 0x70, 0xa8 + }; + + #endregion + + // TODO: Enable the following only if reading from a drive directly + + /* + internal static byte LibCryptDrive(string[] args) + { + byte offset = 0; + string path = $"\\\\.\\{args[0][0]}:"; + byte i; + byte[] sub = new byte[12], buffer = new byte[BUFFER_LEN], buffer2352 = new byte[23520], buffer2 = new byte[BUFFER_LEN], buffer3 = new byte[BUFFER_LEN], buffer4 = new byte[BUFFER_LEN]; + byte[] status; + ushort crc; + uint sector, sector_start, sector_end, a, lcsectors = 0, todo = 9300, done = 0; + + if (args.Length != 1 || (args.Length == 1 && (args[0][1] != 0 && (args[0][1] != ':' || args[0][2] != 0)))) + { + Console.WriteLine("LibCrypt drive detector"); + Console.WriteLine("nUsage: psxt001z.exe --libcryptdrv "); + return 0; + } + + Stream hDevice; + try + { + hDevice = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + } + catch (Exception ex) + { + Console.WriteLine("Can't open device!"); + return 0; + } + + Stream f; + try + { + f = File.Open(F_NAME, FileMode.Open, FileAccess.Write); + } + catch (Exception ex) + { + Console.WriteLine($"Can't open file {F_NAME}!"); + return 0; + } + + byte[] status1 = new byte[4650]; + byte[] status2 = new byte[4650]; + + // Offset detection + Console.WriteLine("Determining offset...\r"); + ScsiPassThroughDirect SRB = new ScsiPassThroughDirect(); + ReadSub(buffer, 0, f, offset, hDevice, SRB); + + switch (buffer[8]) + { + case 0x01: + offset = (byte)(75 - btoi(buffer[9])); + break; + case 0x02: + offset = (byte)(-btoi(buffer[9])); + break; + default: + Console.WriteLine("Can't determine offset!"); + Console.WriteLine(BitConverter.ToString(buffer).Replace('-', ' ')); + return 0; + } + + sub[0] = buffer[0]; + sub[1] = 0x01; + sub[2] = 0x01; + sub[6] = 0x00; + + Console.WriteLine($"Subchannels offset correction: {offset}"); + + // Section 1) 02:58:00 - 03:69:74 -- status1 + // Section 2) 08:58:00 - 09:69:74 -- status2 + + // Step 1 + + for (i = 0; i < CYCLES * 3; i++) + { + + if (todo == 0) + goto end; + + if (i % 3 == 0) + { + sector_start = 13350; + sector_end = 18000; + status = status1; + } + else if (i % 3 == 1) + { + sector_start = 40350; + sector_end = 45000; + status = status2; + } + else + { + Console.WriteLine($"Left: {todo:4} / Flushing cache... \r"); + ClearCache(buffer2352, f, offset, hDevice, SRB); + continue; + } + + for (sector = sector_start; sector < sector_end; sector++) + { + if (status[sector - sector_start] != 0) + continue; + + ReadSub(buffer, sector, f, offset, hDevice, SRB); + Console.WriteLine("Left: %4u / Sector %u... \r", todo, sector); + // generating q-channel + sub[3] = itob((byte)(sector / 60 / 75)); + sub[4] = itob((byte)((sector / 75) % 60)); + sub[5] = itob((byte)(sector % 75)); + sub[7] = itob((byte)((sector + 150) / 60 / 75)); + sub[8] = itob((byte)(((sector + 150) / 75) % 60)); + sub[9] = itob((byte)((sector + 150) % 75)); + crc = CRC16.Calculate(sub, 0, 10); + sub[10] = (byte)(crc >> 8); + sub[11] = (byte)(crc & 0xFF); + if (sub.SequenceEqual(buffer.Take(12))) + { + status[sector - sector_start] = 1; + todo--; + done++; + } + } + } + + // Step 2 + + for (i = 0; i < 2; i++) + { + if (i == 0) + { + sector_start = 13350; + sector_end = 18000; + status = status1; + } + else + { + sector_start = 40350; + sector_end = 45000; + status = status2; + } + + for (sector = sector_start; sector < sector_end; sector++) + { + if (status[sector - sector_start] != 0) + continue; + ReadSub(buffer, sector, f, offset, hDevice, SRB); + Console.WriteLine($"Left: {todo:4} / Sector {sector}... \r"); + + // generating q-channel + sub[3] = itob((byte)(sector / 60 / 75)); + sub[4] = itob((byte)((sector / 75) % 60)); + sub[5] = itob((byte)(sector % 75)); + sub[7] = itob((byte)((sector + 150) / 60 / 75)); + sub[8] = itob((byte)(((sector + 150) / 75) % 60)); + sub[9] = itob((byte)((sector + 150) % 75)); + crc = CRC16.Calculate(sub, 0, 10); + sub[10] = (byte)(crc >> 8); + sub[11] = (byte)(crc ^ 0xFF); + if (sub.SequenceEqual(buffer.Take(12))) + { + Console.WriteLine($"Left: {todo:4} / Sector {sector}: flushing cache... \r"); + do + { + ReadSub(buffer, sector, f, offset, hDevice, SRB); + ClearCache(buffer2352, f, offset, hDevice, SRB); + ReadSub(buffer2, sector, f, offset, hDevice, SRB); + ClearCache(buffer2352, f, offset, hDevice, SRB); + ReadSub(buffer3, sector, f, offset, hDevice, SRB); + ClearCache(buffer2352, f, offset, hDevice, SRB); + } while (!buffer.SequenceEqual(buffer2) || !buffer.SequenceEqual(buffer3)); + //} while (!matrix(buffer, buffer2, buffer3, buffer4, BUFFER_LEN)); + + if (buffer.SequenceEqual(sub)) + { + byte[] buf = Encoding.ASCII.GetBytes($"MSF: {sub[7]:2x}:{sub[8]:2x}:{sub[9]:2x} Q-Data: {BitConverter.ToString(buffer.Take(12).ToArray()).Replace('-', ' ')}"); + f.Write(buf, 0, buf.Length); + lcsectors++; + //fwrite(SRB.SRB_BufPointer, 1, SRB.SRB_BufLen - 4, f); + f.Flush(); + } + } + + todo--; + done++; + } + } + + end: + + Console.WriteLine($"Done! \nProtected sectors: {(lcsectors == 0 ? "None" : lcsectors.ToString())}"); + + f.Close(); + return 1; } - private static byte itob(byte i) + internal static int LibCryptDriveFast(string[] args) { - /* u_char to BCD */ - return (byte)((i) / 10 * 16 + (i) % 10); + Stream f; + Stream hDevice; + ScsiPassThroughDirect SRB; + s8 offset = 0, path[] = "\\\\.\\X:"; + u8 buffer[BUFFER_LEN], buffer2352[23520], sub[12], lc1sectors = 0, lc2sectors = 0, othersectors = 0; + u16 crc; + + if (argc != 1 || (argc == 1 && (args[0][1] != 0 && (args[0][1] != ':' || args[0][2] != 0)))) + { + Console.WriteLine("LibCrypt drive detector (fast)\nUsage: psxt001z.exe --libcryptdrvfast \n"); + return 0; + } + + path[4] = args[0][0]; + + if ((hDevice = CreateFile(path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) + { + Console.WriteLine("Can't open device!\n"); + return 0; + } + + if (fopen_s(&f, F_NAME, "wb") != 0) + { + Console.WriteLine("Can\'t open file %s!\n", F_NAME); + return 0; + } + + // Offset detection + ReadSub(buffer, 0, f, offset, hDevice, SRB); + //if (buffer[5] != buffer[9]) { + // Console.WriteLine("Error determining offset!\nSector 0: %02x%02x%02x %02x:%02x:%02x %02x %02x:%02x:%02x %02x%02x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11]); + //} + switch (buffer[8]) + { + case 0x01: + offset = 75 - btoi(buffer[9]); + break; + case 0x02: + offset = -btoi(buffer[9]); + break; + default: + Console.WriteLine("Can't determine offset!\nSector 0: %02x%02x%02x %02x:%02x:%02x %02x %02x:%02x:%02x %02x%02x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11]); + return 0; + } + Console.WriteLine("Subchannels offset correction: %d\n", offset); + sub[0] = buffer[0]; + sub[1] = 0x01; + sub[2] = 0x01; + sub[6] = 0x00; + + for (int i = 0; i < LIBCRYPT_NUM_SECTORS; i++) + { + Console.WriteLine("\nReading sector %u... ", lc_addresses[i]); + ReadSub(buffer, lc_addresses[i], f, offset, hDevice, SRB); + // generating q-channel + sub[3] = itob(lc_addresses[i] / 60 / 75); + sub[4] = itob((lc_addresses[i] / 75) % 60); + sub[5] = itob(lc_addresses[i] % 75); + sub[7] = itob((lc_addresses[i] + 150) / 60 / 75); + sub[8] = itob(((lc_addresses[i] + 150) / 75) % 60); + sub[9] = itob((lc_addresses[i] + 150) % 75); + crc = crc16(sub, 10); + sub[10] = HIbyte(crc); + sub[11] = LObyte(crc); + for (int a = 1; a <= READ_TIMES; a++) + { + if (!memcmp(sub, buffer, 12)) + { + Console.WriteLine("original sector"); + break; + } + else if (!memcmp(lc1_sectors_contents + (12 * i), buffer, 12)) + { + Console.WriteLine("LibCrypt, LC1 sector"); + fConsole.WriteLine(f, "MSF: %02x:%02x:%02x Q-Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", sub[7], sub[8], sub[9], buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11]); + lc1sectors++; + break; + } + else + { + if (a < READ_TIMES) + { + ClearCache(buffer2352, 0, offset, hDevice, SRB); + continue; + } + else + { + Console.WriteLine("unknown"); + fConsole.WriteLine(f, "MSF: %02x:%02x:%02x Q-Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", sub[7], sub[8], sub[9], buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11]); + othersectors++; + } + } + } + + + + } + Console.WriteLine("\n\nOriginal sectors: %u", LIBCRYPT_NUM_SECTORS - lc1sectors - lc2sectors - othersectors); + Console.WriteLine("\nLC1 sectors: %u", lc1sectors); + Console.WriteLine("\nLC2 sectors: %u", lc2sectors); + Console.WriteLine("\nOther sectors: %u", othersectors); + + fConsole.WriteLine(f, "\nOriginal sectors: %u", LIBCRYPT_NUM_SECTORS - lc1sectors - lc2sectors - othersectors); + fConsole.WriteLine(f, "\nLC1 sectors: %u", lc1sectors); + fConsole.WriteLine(f, "\nLC2 sectors: %u", lc2sectors); + fConsole.WriteLine(f, "\nOther sectors: %u", othersectors); + fclose(f); + return 1; + } + + internal static void ReadSub(byte[] buffer, uint sector, Stream f, byte offset, Stream hDevice, ScsiPassThroughDirect SRB) + { + uint returned; + ZeroMemory(&SRB, sizeof(ScsiPassThroughDirect)); + SRB.Length = sizeof(ScsiPassThroughDirect); + SRB.CDBLength = 12; + SRB.DataIn = SCSI_IOCTL_DATA_IN; + SRB.DataTransferLength = BUFFER_LEN; + SRB.TimeOutValue = 30; + SRB.DataBuffer = buffer; + SRB.CDB[0] = RAW_READ_CMD; + SRB.CDB[2] = HIbyte(HIWORD(sector + offset)); + SRB.CDB[3] = LObyte(HIWORD(sector + offset)); + SRB.CDB[4] = HIbyte(LOWORD(sector + offset)); + SRB.CDB[5] = LObyte(LOWORD(sector + offset)); + SRB.CDB[8] = 1; + SRB.CDB[10] = 1; + + if (!DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &SRB, sizeof(ScsiPassThroughDirect), &SRB, SENSE_SIZE, &returned, 0)) + { + Console.WriteLine("\nError reading subchannel data!\n"); + if (f != 0) + fConsole.WriteLine(f, "Error reading subchannel data!"); + + return 0; + } + + Deinterleave(buffer); + return; + } + + internal static void ClearCache(byte[] buffer, Stream f, byte offset, Stream hDevice, ScsiPassThroughDirect SRB) + { + static uint returned; + for (uint sector = 0; sector < 1000; sector += 10) + { + ZeroMemory(&SRB, sizeof(ScsiPassThroughDirect)); + SRB.Length = sizeof(ScsiPassThroughDirect); + SRB.CDBLength = 12; + SRB.DataIn = SCSI_IOCTL_DATA_IN; + SRB.DataTransferLength = 23520; + SRB.TimeOutValue = 30; + SRB.DataBuffer = buffer; + SRB.CDB[0] = RAW_READ_CMD; + SRB.CDB[2] = HIbyte(HIWORD(sector + offset)); + SRB.CDB[3] = LObyte(HIWORD(sector + offset)); + SRB.CDB[4] = HIbyte(LOWORD(sector + offset)); + SRB.CDB[5] = LObyte(LOWORD(sector + offset)); + SRB.CDB[8] = 10; + SRB.CDB[9] = 0xF8; + + DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &SRB, sizeof(ScsiPassThroughDirect), &SRB, SENSE_SIZE, &returned, 0); + + if (!DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &SRB, sizeof(ScsiPassThroughDirect), &SRB, SENSE_SIZE, &returned, 0)) + { + Console.WriteLine("\nError clearing cache!\n"); + if (f != 0) + fConsole.WriteLine(f, "Error clearing cache!"); + exit(0); + } + } + return; + } + */ + + internal static bool Matrix(byte[] buffer, byte[] buffer2, byte[] buffer3, byte[] buffer4, uint length) + { + for (int i = 0; i < length; i++) + { + if (buffer[i] == buffer2[i]) + { + if (buffer[i] == buffer3[i]) + continue; + if (buffer[i] == buffer4[i]) + continue; + } + else if (buffer[i] == buffer3[i] && buffer[i] == buffer4[i]) + { + continue; + } + else if (buffer2[i] == buffer3[i] && buffer2[i] == buffer4[i]) + { + continue; + } + + return false; + } + + return true; + } + + internal static void Deinterleave(byte[] buffer) + { + byte[] buffertmp = new byte[12]; + for (int i = 0; i < 12; i++) + { + buffertmp[i] |= (byte)((buffer[i * 8] & 0x40) << 1); + buffertmp[i] |= (byte)((buffer[i * 8 + 1] & 0x40)); + buffertmp[i] |= (byte)((buffer[i * 8 + 2] & 0x40) >> 1); + buffertmp[i] |= (byte)((buffer[i * 8 + 3] & 0x40) >> 2); + buffertmp[i] |= (byte)((buffer[i * 8 + 4] & 0x40) >> 3); + buffertmp[i] |= (byte)((buffer[i * 8 + 5] & 0x40) >> 4); + buffertmp[i] |= (byte)((buffer[i * 8 + 6] & 0x40) >> 5); + buffertmp[i] |= (byte)((buffer[i * 8 + 7] & 0x40) >> 6); + } + + Array.Copy(buffertmp, buffer, 12); + return; + } + + internal static bool LibCryptDetect(string subPath, string sbiPath) + { + if (string.IsNullOrWhiteSpace(subPath) || !File.Exists(subPath)) + return false; + + // Variables + byte[] buffer = new byte[16], sub = new byte[16];//, pregap = 0; + uint sector, psectors = 0, tpos = 0; + + // Opening .sub + Stream subfile = File.OpenRead(subPath); + + // checking extension + if (Path.GetExtension(subPath).TrimStart('.').ToLowerInvariant() != "sub") + { + Console.WriteLine($"{subPath}: unknown file extension"); + return false; + } + + // filesize + long size = subfile.Length; + if (size % 96 != 0) + { + Console.WriteLine($"{subfile}: wrong size"); + return false; + } + + // sbi + Stream sbi = null; + if (sbiPath != null) + { + sbi = File.OpenWrite(sbiPath); + sbi.Write(Encoding.ASCII.GetBytes("SBI\0"), 0, 4); + } + + for (sector = 150; sector < ((size / 96) + 150); sector++) + { + subfile.Seek(12, SeekOrigin.Current); + if (subfile.Read(buffer, 0, 12) != 12) + return true; + + subfile.Seek(72, SeekOrigin.Current); + + // New track + if ((btoi(buffer[1]) == (btoi(sub[1]) + 1)) && (buffer[2] == 0 || buffer[2] == 1)) + { + Array.Copy(buffer, sub, 6); + tpos = (uint)((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]); + } + + // New index + else if (btoi(buffer[2]) == (btoi(sub[2]) + 1) && buffer[1] == sub[1]) + { + Array.Copy(buffer, 2, sub, 2, 4); + tpos = (uint)((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]); + } + + // MSF1 [3-5] + else + { + if (sub[2] == 0) + tpos--; + else + tpos++; + + sub[3] = itob((byte)(tpos / 60 / 75)); + sub[4] = itob((byte)((tpos / 75) % 60)); + sub[5] = itob((byte)(tpos % 75)); + } + + //MSF2 [7-9] + sub[7] = itob((byte)(sector / 60 / 75)); + sub[8] = itob((byte)((sector / 75) % 60)); + sub[9] = itob((byte)(sector % 75)); + + // CRC-16 [10-11] + ushort crc = CRC16.Calculate(sub, 0, 10); + sub[10] = (byte)(crc >> 8); + sub[11] = (byte)(crc & 0xFF); + + //if (buffer[10] != sub[10] && buffer[11] != sub[11] && (buffer[3] != sub[3] || buffer[7] != sub[7] || buffer[4] != sub[4] || buffer[8] != sub[8] || buffer[5] != sub[5] || buffer[9] != sub[9])) { + //if (buffer[10] != sub[10] || buffer[11] != sub[11] || buffer[3] != sub[3] || buffer[7] != sub[7] || buffer[4] != sub[4] || buffer[8] != sub[8] || buffer[5] != sub[5] || buffer[9] != sub[9]) { + if (!buffer.Take(6).SequenceEqual(sub.Take(6)) || !buffer.Skip(7).Take(5).SequenceEqual(sub.Skip(7).Take(5))) + { + Console.WriteLine($"MSF: {sub[7]:2x}:{sub[8]:2x}:{sub[9]:2x} Q-Data: {buffer[0]:2x}{buffer[1]:2x}{buffer[2]:2x} {buffer[3]:2x}:{buffer[4]:2x}:{buffer[5]:2x} {buffer[6]:2x} {buffer[7]:2x}:{buffer[8]:2x}:{buffer[9]:2x} {buffer[10]:2x}{buffer[11]:2x} xor {crc ^ ((buffer[10] << 8) + buffer[11]):4x} {CRC16.Calculate(buffer, 0, 10) ^ ((buffer[10] << 8) + buffer[11]):4x}"); + //Console.WriteLine("\nMSF: %02x:%02x:%02x Q-Data: %02x%02x%02x %02x:%02x:%02x %02x %02x:%02x:%02x %02x%02x", sub[7], sub[8], sub[9], sub[0], sub[1], sub[2], sub[3], sub[4], sub[5], sub[6], sub[7], sub[8], sub[9], sub[10], sub[11]); + + if (buffer[3] != sub[3] && buffer[7] != sub[7] && buffer[4] == sub[4] && buffer[8] == sub[8] && buffer[5] == sub[5] && buffer[9] == sub[9]) + Console.WriteLine($" P1 xor {buffer[3] ^ sub[3]:2x} {buffer[7] ^ sub[7]:2x}"); + else if (buffer[3] == sub[3] && buffer[7] == sub[7] && buffer[4] != sub[4] && buffer[8] != sub[8] && buffer[5] == sub[5] && buffer[9] == sub[9]) + Console.WriteLine($" P2 xor {buffer[4] ^ sub[4]:2x} {buffer[8] ^ sub[8]:2x}"); + else if (buffer[3] == sub[3] && buffer[7] == sub[7] && buffer[4] == sub[4] && buffer[8] == sub[8] && buffer[5] != sub[5] && buffer[9] != sub[9]) + Console.WriteLine($" P3 xor {buffer[5] ^ sub[5]:2x} {buffer[9] ^ sub[9]:2x}"); + else + Console.WriteLine(" ?"); + + Console.WriteLine("\n"); + psectors++; + if (sbi != null) + { + sbi.Write(sub, 7, 3); + sbi.Write(new byte[] { 0x01 }, 0, 1); + sbi.Write(buffer, 0, 10); + } + } + } + // } + + Console.WriteLine($"Number of modified sectors: {psectors}"); + return true; + } + + internal static int XorLibCrypt() + { + sbyte b; + byte d; + byte i, a, x; + byte[] sub = new byte[12] + { + 0x41, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + ushort crc; + for (i = 0; i < LIBCRYPT_NUM_SECTORS; i++) + { + sub[3] = itob((byte)(lc_addresses[i] / 60 / 75)); + sub[4] = itob((byte)((lc_addresses[i] / 75) % 60)); + sub[5] = itob((byte)(lc_addresses[i] % 75)); + sub[7] = itob((byte)((lc_addresses[i] + 150) / 60 / 75)); + sub[8] = itob((byte)(((lc_addresses[i] + 150) / 75) % 60)); + sub[9] = itob((byte)((lc_addresses[i] + 150) % 75)); + crc = CRC16.Calculate(sub, 0, 10); + sub[10] = (byte)(crc >> 8); + sub[11] = (byte)(crc & 0xFF); + + Console.WriteLine($"%u %02x:%02x:%02x", lc_addresses[i], sub[7], sub[8], sub[9]); + Console.WriteLine($" %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x", sub[0], sub[1], sub[2], sub[3], sub[4], sub[5], sub[6], sub[7], sub[8], sub[9], sub[10], sub[11]); + Console.WriteLine($" %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x", lc1_sectors_contents[i * 12], lc1_sectors_contents[(i * 12) + 1], lc1_sectors_contents[(i * 12) + 2], lc1_sectors_contents[(i * 12) + 3], lc1_sectors_contents[(i * 12) + 4], lc1_sectors_contents[(i * 12) + 5], lc1_sectors_contents[(i * 12) + 6], lc1_sectors_contents[(i * 12) + 7], lc1_sectors_contents[(i * 12) + 8], lc1_sectors_contents[(i * 12) + 9], lc1_sectors_contents[(i * 12) + 10], lc1_sectors_contents[(i * 12) + 11]); + + d = 0; + + for (a = 3; a < 12; a++) + { + x = (byte)(lc1_sectors_contents[(i * 12) + a] ^ sub[a]); + Console.WriteLine($" %x%x%x%x%x%x%x%x", (x >> 7) & 0x1, (x >> 6) & 0x1, (x >> 5) & 0x1, (x >> 4) & 0x1, (x >> 3) & 0x1, (x >> 2) & 0x1, (x >> 1) & 0x1, x & 0x1); + if (x == 0) + continue; + for (b = 7; b >= 0; b--) + { + if (((x >> b) & 0x1) != 0) + { + d = (byte)(d << 1); + d |= (byte)((sub[a] >> b) & 0x1); + } + } + } + + Console.WriteLine($" {(d >> 3) & 0x1:x}{(d >> 2) & 0x1:x}{(d >> 1) & 0x1:x}{d & 0x1}"); + } + + return 1; } } } diff --git a/BurnOutSharp/External/psxt001z/Main.cs b/BurnOutSharp/External/psxt001z/Main.cs new file mode 100644 index 00000000..01ca2ec9 --- /dev/null +++ b/BurnOutSharp/External/psxt001z/Main.cs @@ -0,0 +1,1004 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using static psxt001z.Common; +using static psxt001z.Functions; + +namespace psxt001z +{ + public class Main + { + public static int main(string[] args) + { + Console.WriteLine($"psxt001z by Dremora, {VERSION}"); + Console.WriteLine(); + + if (args == null || args.Length == 0) + { + Help(); + return -1; + } + + if (args.Length == 1) + GetInfo(args[0]); + + string feature = args[0]; + switch (feature) + { + case "--checksums": + case "-c": + Checksums(args); + break; + + case "--libcrypt": + case "-l": + { + if (args.Length != 1 && args.Length != 2) + { + LibCryptHelp(); + return 0; + } + + string subPath = args[0]; + string sbiPath = null; + if (args.Length == 2) + sbiPath = args[1]; + + if (!LibCrypt.LibCryptDetect(subPath, sbiPath)) + LibCryptHelp(); + + break; + } + + case "--xorlibcrypt": + LibCrypt.XorLibCrypt(); + break; + + case "--zektor": + ClearEDCData(args[1]); + break; + + case "--antizektor": + SetEDCData(args[1]); + break; + + case "--patch": + { + string output = args[1]; + string input = args[2]; + int skip = 0; + if (args.Length == 4) + skip = int.Parse(args[3]); + + Patch(output, input, skip); + break; + } + + case "--resize": + { + string input = args[1]; + long newsize = long.Parse(args[2]); + Resize(input, newsize); + break; + } + + case "--track": + { + string filename = args[1]; + int start = int.Parse(args[2]); + int size = int.Parse(args[3]); + uint crc = uint.Parse(args[4]); + + bool isRiff = false; + bool? mode = null; + string output = null; + for (int i = 5; i < args.Length; i++) + { + if (args[i] == "r") + isRiff = true; + else if (args[i][0] == '+') + mode = true; + else if (args[i][1] == '-') + mode = false; + else if (args[i] == "s") + output = args[++i]; + } + + Track trackfix = new Track(filename, start, size, crc, isRiff, mode, output); + while (!trackfix.FindTrack()) ; + trackfix.Done(); + + break; + } + + case "--str": + SplitStr(args); + break; + + case "--str2bs": + StrToBs(args); + break; + + case "--gen": + Generate(args); + break; + + case "--scan": + Info.GetInfo(args[2], false); + break; + + case "--fix": + Info.GetInfo(args[2], true); + break; + + case "--sub": + CreateSubchannel(args[2], args[3]); + break; + + case "--m3s": + M3S(args[2]); + break; + + case "--matrix": + { + Matrix(args); + break; + } + + //case "--libcryptdrv": + // LibCrypt.LibCryptDrive(argv + 2); + // break; + //case "--libcryptdrvfast": + // LibCrypt.LibCryptDriveFast(argv + 2); + // break; + + default: + Help(); + break; + } + + return 1; + } + + public static void GetInfo(string filename) + { + Stream image; + try + { + image = File.OpenRead(filename); + } + catch (Exception ex) + { + Console.WriteLine($"Error opening file \"{filename}\"! {ex}"); + return; + } + + FileTools file = new FileTools(image); + long imagesize = image.Length; + + Console.WriteLine($"File: {filename}"); + if (imagesize % 2352 != 0) + { + Console.WriteLine($"File \"{filename}\" is not Mode2/2352 image!"); + image.Close(); + return; + } + + long realsectors = file.imagesize(); + long imagesectors = imagesize / 2352; + long realsize = realsectors * 2352; + if (imagesize == realsize) + { + Console.WriteLine($"Size (bytes): {imagesize} (OK)"); + Console.WriteLine($"Size (sectors): {imagesectors} (OK)"); + } + else + { + Console.WriteLine($"Size (bytes): {imagesize}"); + Console.WriteLine($"From image: {realsize}"); + Console.WriteLine($"Size (sectors): {imagesectors}"); + Console.WriteLine($"From image: {realsectors}"); + } + + Console.WriteLine($"EDC in Form 2 sectors: {(GetEDCStatus(image) ? "YES" : "NO")}"); + + string exe = file.exe(); + Console.WriteLine($"ID: {exe.Substring(0, 4)}-{exe.Substring(5)}"); + Console.WriteLine($"Date: {file.date()}"); + + Console.Write("System area: "); + image.Seek(0, SeekOrigin.Begin); + + byte[] buffer = new byte[2352]; + CRC32 crc = new CRC32(); + for (uint i = 0; i < 16; i++) + { + image.Read(buffer, 0, 2352); + crc.ProcessCRC(buffer, 0, 2352); + } + + uint imagecrc = crc.m_crc32; + Console.WriteLine($"{Info.GetEdcType(imagecrc)}"); + Console.WriteLine(); + + image.Close(); + return; + } + + public static void Help() + { + Console.Write("Usage:\n"); + + Console.Write("======\n"); + + Console.Write("psxt001z.exe image.bin\n"); + Console.Write(" Display image's info.\n\n"); + + Console.Write("psxt001z.exe --scan image.bin\n"); + Console.Write(" Scan image.bin postgap for errors.\n\n"); + + Console.Write("psxt001z.exe --fix image.bin\n"); + Console.Write(" Scan image.bin postgap for errors and fix them.\n\n"); + + //Console.Write("psxt001z.exe --libcryptdrvfast \n"); + //Console.Write(" Check subchannels for LibCrypt protection using new detection\n method (disc).\n\n"); + + Console.Write("psxt001z.exe --checksums file [start [end]]\n"); + Console.Write(" Calculate file's checksums (CRC-32, MD5, SHA-1).\n"); + Console.Write(" [in] file Specifies the file, which checksums will be calculated.\n"); + Console.Write(" start Specifies start position for checksums calculation.\n"); + Console.Write(" size Specifies size of block for checksums calculation.\n\n"); + + Console.Write("psxt001z.exe --zektor image.bin\n"); + Console.Write(" Zektor. Replace EDC in Form 2 Mode 2 sectors with zeroes.\n\n"); + + Console.Write("psxt001z.exe --antizektor image.bin\n"); + Console.Write(" Antizektor. Restore EDC in Form 2 Mode 2 sectors.\n\n"); + + Console.Write("psxt001z.exe --resize image.bin size\n"); + Console.Write(" Resize file to requested size.\n\n"); + + Console.Write("psxt001z.exe --patch image.bin patch.bin offset\n"); + Console.Write(" Insert patch.bin into image.bin, skipping given number of bytes from the\n offset.\n\n"); + + Console.Write("psxt001z.exe --track image.bin bytes_to_skip size crc-32 [r] [+/-/f] [s filename]\n"); + Console.Write(" Try to guess an offset correction of the image dump by searching a track with\n given size and CRC-32.\n r - Calculate crc with RIFF header.\n +/- - Search only for positive or negative offset correction.\n s - Save track with given filename.\n\n"); + + Console.Write("psxt001z.exe --gen file.bin filesize [-r]\n"); + Console.Write(" Generate a file of the requested size.\n -r - add RIFF header.\n\n"); + + Console.Write("psxt001z.exe --str file.str video.str audio.xa\n"); + Console.Write(" Deinterleave file.str to video.str and audio.xa.\n\n"); + + Console.Write("psxt001z.exe --str2bs file.str\n"); + Console.Write(" Convert file.str to .bs-files.\n\n"); + + Console.Write("psxt001z.exe --sub subchannel.sub size\n"); + Console.Write(" Generate RAW subchannel with given size (in sectors).\n\n"); + + Console.Write("psxt001z.exe --m3s subchannel.m3s\n"); + Console.Write(" Generate M3S subchannel.\n\n"); + + Console.Write("psxt001z.exe --libcrypt []\n"); + Console.Write("Usage: psxt001z.exe --libcrypt []\n"); + Console.Write(" Check subchannels for LibCrypt protection. (file)\n"); + Console.Write(" [in] Specifies the subchannel file to be scanned.\n"); + Console.Write(" [out] Specifies the subchannel file in SBI format where protected\n sectors will be written.\n\n"); + + //Console.Write("psxt001z.exe --libcryptdrv \n"); + //Console.Write(" Check subchannels for LibCrypt protection (disc).\n\n"); + + Console.Write("Press any key to continue..."); + Console.ReadKey(); + } + + public static bool Patch(string output, string input, int skip = 0) + { + Stream f1, f2; + try + { + f1 = File.Open(output, FileMode.Open, FileAccess.ReadWrite); + f2 = File.Open(input, FileMode.Open, FileAccess.Read); + + f1.Seek(skip, SeekOrigin.Begin); + + Console.WriteLine($"Patching \"{output}\" with \"{input}\", skipping {skip} bytes..."); + + int i = 0; + while (f1.Position < f1.Length && f2.Position < f2.Length) + { + byte[] buffer = new byte[1]; + f2.Read(buffer, 0, 1); + f1.Write(buffer, 0, 1); + i++; + } + + Console.WriteLine("Done!"); + Console.WriteLine(); + Console.WriteLine($"{i} bytes were replaced"); + Console.WriteLine("File was successully patched!"); + + return true; + } + catch (Exception ex) + { + Console.WriteLine(ex); + return false; + } + } + + public static bool Resize(string input, long newsize) + { + Stream f; + try + { + f = File.Open(input, FileMode.Open, FileAccess.ReadWrite); + } + catch (Exception ex) + { + Console.WriteLine(ex); + return false; + } + + FileTools image = new FileTools(f); + switch (image.resize(newsize)) + { + case 0: + Console.Write($"File's \"{input}\" size is already {newsize} bytes!"); + break; + case 1: + Console.Write($"File \"{input}\" was successfully resized to {newsize} bytes!"); + break; + case 2: + Console.Write($"File \"{input}\" was successfully truncated to {newsize} bytes!"); + break; + } + + return true; + } + + public static bool Copy(string input, string output, long startbyte, long length) + { + Stream infile; + try + { + infile = File.Open(input, FileMode.Open, FileAccess.Read); + } + catch (Exception ex) + { + Console.WriteLine($"File {input} can't be found."); + return true; + } + + return true; + //HANDLE hfile = CreateFileW(argv[1], GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0); + //SetFilePointer(hfile, newsize, 0, FILE_BEGIN); + //SetEndOfFile(hfile); + } + + /// + /// Generate EDC data for a Mode2/2352 image + /// + public static void SetEDCData(string filename) + { + Stream image = File.Open(filename, FileMode.Open, FileAccess.ReadWrite); + + byte[] ecc_f_lut = new byte[256]; + byte[] ecc_b_lut = new byte[256]; + int[] edc_lut = new int[256]; + + for (int a = 0; a < 256; a++) + { + int b = ((a << 1) ^ ((a & 0x80) != 0 ? 0x11D : 0)); + + ecc_f_lut[a] = (byte)b; + ecc_b_lut[a ^ b] = (byte)a; + + int edc_init = a; + for (b = 0; b < 8; b++) + { + edc_init = (int)((edc_init >> 1) ^ ((edc_init & 1) != 0 ? 0xD8018001 : 0)); + } + + edc_lut[a] = edc_init; + } + + long filesize = image.Length; + if (filesize % 2352 != 0) + { + Console.Write($"File '{filename}' is not Mode2/2352 image!"); + image.Close(); + return; + } + + long sectors = filesize / 2352; + Console.WriteLine("Converting image..."); + for (long sector = 0; sector < sectors; sector++) + { + image.Seek(sector * 2352 + 18, SeekOrigin.Begin); + + byte[] z = new byte[1]; + image.Read(z, 0, 1); + if ((z[0] >> 5 & 0x1) != 0) + { + image.Seek(-3, SeekOrigin.Current); + + byte[] buffer = new byte[2332]; + image.Read(buffer, 0, 2332); + image.Seek(0, SeekOrigin.Current); + + buffer = BitConverter.GetBytes(CalculateEDC(buffer, 0, 2332, edc_lut)); + image.Write(buffer, 0, 4); + } + } + + image.Close(); + Console.WriteLine("Done!"); + return; + } + + /// + /// Calculate CRC-32, MD5, and SHA-1 checksums + /// + public static byte Checksums(string[] args) + { + if (args.Length < 3 || args.Length > 5) + { + ChecksumsHelp(); + return 0; + } + + // Opening file + Stream file = File.OpenRead(args[2]); + Console.WriteLine($"File: {args[2]}"); + + double percents = 0; + + long filesize = file.Length; + + long start; + if (args.Length > 3) + { + start = long.Parse(args[3]); + if (start >= filesize) + { + Console.WriteLine("Error: start position can't be larger than filesize!"); + return 0; + } + + Console.WriteLine($"Start: {start}"); + } + else + { + start = 0; + } + + long block; + if (args.Length > 4) + { + block = long.Parse(args[4]); + if (block > filesize) + { + Console.WriteLine("Error: block size can't be larger than filesize!"); + return 0; + } + + if (block == 0) + { + Console.WriteLine("Error: block size can't equal with zero!"); + return 0; + } + } + else + { + block = filesize - start; + } + + if (block + start > filesize) + { + Console.WriteLine("Error: block size and start position can't be larger than file size!"); + return 0; + } + + Console.Write($"Size: {block}"); + long total = (long)Math.Ceiling((double)block / 1024); + + // checksums + byte[] Message_Digest = new byte[20]; + byte[] buffer = new byte[1024], digest = new byte[16]; + int len; + + CRC32 crc = new CRC32(); + MD5 md5 = MD5.Create(); + md5.Initialize(); + SHA1 sha1 = SHA1.Create(); + + file.Seek(start, SeekOrigin.Begin); + + for (uint i = 0; i < total; i++) + { + if (i * 100 / total > percents) + { + percents = i * 100 / total; + Console.Write($"\rCalculating checksums: {percents}%"); + } + + len = file.Read(buffer, 0, 1024); + + if (block <= len) + { + len = (int)block; + block = 0; + } + else + { + block -= 1024; + } + + md5.TransformBlock(buffer, 0, len, null, 0); + sha1.TransformBlock(buffer, 0, len, null, 0); + crc.ProcessCRC(buffer, 0, len); + } + + md5.TransformFinalBlock(digest, 0, digest.Length); + sha1.TransformFinalBlock(Message_Digest, 0, Message_Digest.Length); + + Console.Write($"\rCRC-32: {crc.m_crc32:8x} \n"); + Console.Write($"MD5: {BitConverter.ToString(md5.Hash).Replace("-", string.Empty)}"); + Console.WriteLine($"MD5: {BitConverter.ToString(md5.Hash).Replace("-", string.Empty)}"); + Console.WriteLine($"SHA-1: {BitConverter.ToString(sha1.Hash).Replace("-", string.Empty)}"); + Console.WriteLine(); + return 1; + } + + /// + /// Generate missing RIFF headers + /// + public static void Generate(string[] args) + { + Stream f = File.OpenWrite(args[2]); + byte[] riff = new byte[44]; + long size = long.Parse(args[3]); + + if (args.Length == 5) + { + if (args[4] == "-r") + { + riff[0] = 0x52; + riff[1] = 0x49; + riff[2] = 0x46; + riff[3] = 0x46; + riff[4] = (byte)((size - 8) & 0xFF); + riff[5] = (byte)((size - 8) >> 8); + riff[6] = (byte)((size - 8) >> 16); + riff[7] = (byte)((size - 8) >> 24); + riff[8] = 0x57; + riff[9] = 0x41; + riff[10] = 0x56; + riff[11] = 0x45; + riff[12] = 0x66; + riff[13] = 0x6D; + riff[14] = 0x74; + riff[15] = 0x20; + riff[16] = 0x10; + riff[17] = 0x00; + riff[18] = 0x00; + riff[19] = 0x00; + riff[20] = 0x01; + riff[21] = 0x00; + riff[22] = 0x02; + riff[23] = 0x00; + riff[24] = 0x44; + riff[25] = 0xAC; + riff[26] = 0x00; + riff[27] = 0x00; + riff[28] = 0x10; + riff[29] = 0xB1; + riff[30] = 0x02; + riff[31] = 0x00; + riff[32] = 0x04; + riff[33] = 0x00; + riff[34] = 0x10; + riff[35] = 0x00; + riff[36] = 0x64; + riff[37] = 0x61; + riff[38] = 0x74; + riff[39] = 0x61; + riff[40] = (byte)((size - 44) & 0xFF); + riff[41] = (byte)((size - 44) >> 8); + riff[42] = (byte)((size - 44) >> 16); + riff[43] = (byte)((size - 44) >> 24); + + f.Write(riff, 0, 44); + } + } + + f.Seek(size - 1, SeekOrigin.Begin); + f.WriteByte(0x00); + f.Close(); + + Console.WriteLine($"File '{args[2]}' with size {size} bytes was successfully generated!"); + return; + } + + /// + /// Create a generic M3S subchannel file for a sector count + /// + /// + public static void M3S(string filename) + { + byte[] buffer = { 0x41, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + Stream subchannel = File.Open(filename, FileMode.Create, FileAccess.Write); + Console.Write($"File: {filename}"); + + for (long sector = 13350, sector2 = sector + 150; sector < 17850; sector++, sector2++) + { + double mindbl = sector / 60 / 75; + byte min = (byte)Math.Floor(mindbl); + + double secdbl = (sector - (min * 60 * 75)) / 75; + byte sec = (byte)Math.Floor(secdbl); + + byte frame = (byte)(sector - (min * 60 * 75) - (sec * 75)); + + buffer[3] = itob(min); + buffer[4] = itob(sec); + buffer[5] = itob(frame); + + mindbl = sector2 / 60 / 75; + min = (byte)Math.Floor(mindbl); + + secdbl = (sector2 - (min * 60 * 75)) / 75; + sec = (byte)Math.Floor(secdbl); + + frame = (byte)(sector2 - (min * 60 * 75) - (sec * 75)); + + buffer[7] = itob(min); + buffer[8] = itob(sec); + buffer[9] = itob(frame); + + ushort crc = CRC16.Calculate(buffer, 0, 10); + subchannel.Write(buffer, 0, 10); + subchannel.WriteByte((byte)(crc >> 8)); + subchannel.WriteByte((byte)(crc & 0xFF)); + + for (int i = 0; i < 4; i++) + { + subchannel.WriteByte(0x00); + } + + Console.WriteLine($"Creating M3S: {100 * sector}%\r"); + } + + Console.WriteLine("Creating M3S: 100%"); + + subchannel.Close(); + + Console.WriteLine("Done!"); + return; + } + + /// + /// Matrix 3 input files into a single output + /// + public static void Matrix(string[] args) + { + Stream f1 = File.Open(args[2], FileMode.Open, FileAccess.ReadWrite); + Stream f2 = File.Open(args[3], FileMode.Open, FileAccess.ReadWrite); + Stream f3 = File.Open(args[4], FileMode.Open, FileAccess.ReadWrite); + Stream f4 = File.Open(args[5], FileMode.Create, FileAccess.Write); + + long subsize = f1.Length; + for (long i = 0; i < subsize; i++) + { + byte[] r1 = new byte[1]; + f1.Read(r1, 0, 1); + + byte[] r2 = new byte[1]; + f2.Read(r2, 0, 1); + + byte[] r3 = new byte[1]; + f3.Read(r3, 0, 1); + + if (r1 == r2) + { + f4.Write(r1, 0, 1); + } + else if (r1 == r3) + { + f4.Write(r1, 0, 1); + } + else if (r2 == r3) + { + f4.Write(r2, 0, 1); + } + else + { + Console.WriteLine($"Byte 0x{i:x} ({i}) is different!"); + Console.WriteLine($"{args[2]}: {r1[0]:2x}"); + Console.WriteLine($"{args[3]}: {r2[0]:2x}"); + Console.WriteLine($"{args[4]}: {r3[0]:2x}"); + Console.WriteLine(); + return; + } + } + + Console.WriteLine("Done!"); + return; + } + + /// + /// Split a STR-formatted image into audio and video + /// + public static void SplitStr(string[] argv) + { + Stream str = File.OpenRead(argv[2]); + long filesize = str.Length; + if (filesize % 2336 != 0) + { + Console.Write($"File '{argv[2]}' is not in STR format!"); + str.Close(); + return; + } + + long sectors = filesize / 2336; + Stream video = File.OpenWrite(argv[3]); + + sectors = filesize / 2336; + Stream audio = File.OpenWrite(argv[4]); + + for (long i = 0; i < sectors; i++) + { + str.Seek(2, SeekOrigin.Current); + + byte[] ctrlbyte = new byte[1]; + str.Read(ctrlbyte, 0, 1); + + byte[] buffer = new byte[2336]; + if ((ctrlbyte[0] >> 5 & 0x1) != 0) + { + str.Seek(-3, SeekOrigin.Current); + str.Read(buffer, 0, 2336); + audio.Write(buffer, 0, 2336); + } + else + { + str.Seek(5, SeekOrigin.Current); + str.Read(buffer, 0, 2048); + video.Write(buffer, 0, 2048); + str.Seek(280, SeekOrigin.Current); + } + } + + str.Close(); + audio.Close(); + video.Close(); + + Console.WriteLine("Done!"); + + return; + } + + /// + /// Split a STR-formatted file into BS-formatted blocks + /// + public static void StrToBs(string[] argv) + { + byte[] buffer = new byte[2016]; + int filenamesize = argv[2].Length; + string directory = $"{argv[2]}-bs"; + + Stream str = File.OpenRead(argv[2]); + long filesize = str.Length; + if (filesize % 2048 != 0) + { + Console.Write($"File '{argv[2]}; is not in STR format!"); + str.Close(); + return; + } + + /*wchar_t newfilename[2048]; + for (u8 f = 0; f < strlen(filename); f++) { + newfilename[f] = filename[f]; + *((char *)newfilename +f*2 +1) = 0; + }*/ + + Directory.CreateDirectory(directory); + + long numblocks = filesize / 2048; + Stream bs = File.OpenWrite(directory + "\\000001.bs"); + + ushort a = 1; + ushort b = 0; + ushort c = 0; + + byte[] ax = { 0, 0 }; + byte[] bx = { 0, 0 }; + byte[] cx = { 0, 0 }; + + str.Seek(32, SeekOrigin.Current); + str.Read(buffer, 0, 2016); + Console.WriteLine(directory); + + bs.Write(buffer, 0, 2016); + Console.WriteLine("2"); + Console.WriteLine($"Creating: {directory}\\000001.bs"); + + for (uint i = 1; i < numblocks; i++) + { + str.Seek(1, SeekOrigin.Current); + + byte[] byt = new byte[1]; + str.Read(byt, 0, 1); + + if (byt[0] == 0) + { + bs.Close(); + bs = File.OpenWrite(directory + $"\\{i.ToString().PadLeft(6, '0')}.bs"); + Console.WriteLine($"Creating: {directory}\\{i.ToString().PadLeft(6, '0')}.bs"); + } + + str.Seek(30, SeekOrigin.Current); + str.Read(buffer, 0, 2016); + bs.Write(buffer, 0, 2016); + } + + bs.Close(); + str.Close(); + + Console.WriteLine(); + Console.WriteLine("Done!"); + + return; + } + + /// + /// Create a generic SUB subchannel file for a sector count + /// + public static void CreateSubchannel(string filename, string strsectors) + { + long sectors = long.Parse(strsectors); + if (sectors == 0 || sectors == -1) + { + Console.WriteLine("Wrong size!"); + return; + } + + Stream subchannel = File.Open(filename, FileMode.Create, FileAccess.Write); + + Console.Write($"File: {filename}"); + Console.Write($"Size (bytes): {sectors * 96}"); + Console.Write($"Size (sectors): {sectors}"); + + byte[] buffer = new byte[10]; + buffer[0] = 0x41; + buffer[1] = 0x01; + buffer[2] = 0x01; + buffer[6] = 0x00; + + for (long sector = 0, sector2 = 150; sector < sectors; sector++, sector2++) + { + /*if (sector2 == 4350) { + buffer[1] = 0x02; + sector = 0; + }*/ + + double mindbl = sector / 60 / 75; + byte min = (byte)Math.Floor(mindbl); + + double secdbl = (sector - (min * 60 * 75)) / 75; + byte sec = (byte)Math.Floor(secdbl); + + byte frame = (byte)(sector - (min * 60 * 75) - (sec * 75)); + + buffer[3] = itob(min); + buffer[4] = itob(sec); + buffer[5] = itob(frame); + + mindbl = sector2 / 60 / 75; + min = (byte)Math.Floor(mindbl); + + secdbl = (sector2 - (min * 60 * 75)) / 75; + sec = (byte)Math.Floor(secdbl); + + frame = (byte)(sector2 - (min * 60 * 75) - (sec * 75)); + + buffer[7] = itob(min); + buffer[8] = itob(sec); + buffer[9] = itob(frame); + + ushort crc = CRC16.Calculate(buffer, 0, 10); + + for (int i = 0; i < 12; i++) + { + subchannel.WriteByte(0x00); + } + + subchannel.Write(buffer, 0, 10); + subchannel.WriteByte((byte)(crc >> 8)); + subchannel.WriteByte((byte)(crc & 0xFF)); + + for (int i = 0; i < 72; i++) + { + subchannel.WriteByte(0x00); + } + + Console.Write("Creating: %02u%%\r", (100 * sector) / sectors); + } + + subchannel.Seek(0, SeekOrigin.Begin); + for (int i = 0; i < 12; i++) + { + subchannel.WriteByte(0xFF); + } + + Console.WriteLine("Creating: 100%"); + + subchannel.Close(); + + Console.WriteLine("Done!"); + return; + } + + /// + /// Clear EDC data from a Mode2/2352 image + /// + public static void ClearEDCData(string filename) + { + byte[] zero = { 0x00, 0x00, 0x00, 0x00 }; + + Stream image = File.Open(filename, FileMode.Open, FileAccess.ReadWrite); + long filesize = image.Length; + if (filesize % 2352 != 0) + { + Console.WriteLine($"File '{filename}' is not Mode2/2352 image!"); + image.Close(); + return; + } + + Console.WriteLine("Converting image..."); + + long sectors = filesize / 2352; + for (long sector = 0; sector < sectors; sector++) + { + image.Seek(sector * 2352 + 18, SeekOrigin.Begin); + + byte[] z = new byte[1]; + image.Read(z, 0, 1); + if ((z[0] >> 5 & 0x1) != 0) + { + image.Seek(2329, SeekOrigin.Current); + image.Write(zero, 0, 4); + } + } + + image.Close(); + Console.WriteLine("Done!"); + return; + } + + #region Help + + private static void ChecksumsHelp() + { + Console.WriteLine("psxt001z.exe --checksums file [start [end]]"); + Console.WriteLine(" Calculate file's checksums (CRC-32, MD5, SHA-1)."); + Console.WriteLine(" [in] file Specifies the file, which checksums will be calculated."); + Console.WriteLine(" start Specifies start position for checksums calculation."); + Console.WriteLine(" size Specifies size of block for checksums calculation."); + Console.WriteLine(); + } + + private static void LibCryptHelp() + { + Console.WriteLine("LibCrypt detector\nUsage: psxt001z.exe --libcrypt []"); + Console.WriteLine(" Check subchannel for LibCrypt protection."); + Console.WriteLine(" [in] Specifies the subchannel file to be scanned."); + Console.WriteLine(" [out] Specifies the subchannel file in SBI format where protected\n sectors will be written."); + Console.WriteLine(); + } + + #endregion + } +} diff --git a/BurnOutSharp/External/psxt001z/Scramble.cs b/BurnOutSharp/External/psxt001z/Scramble.cs new file mode 100644 index 00000000..b3194e51 --- /dev/null +++ b/BurnOutSharp/External/psxt001z/Scramble.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using static psxt001z.Common; + +namespace psxt001z +{ + public class Scramble + { + private static readonly byte[] sync = new byte[12] { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }; + + public int __main(string[] args) + { + if (args.Length < 2 || args.Length > 3) + { + Console.WriteLine("Syntax: px_p8 [-t] filename"); + return 1; + } + + int sectors; + if (args.Length == 2 && args[0] == "-t") + { + args[0] = args[1]; + sectors = 2352; + } + else + { + sectors = 4704; + } + + Stream sector_file; + try + { + sector_file = File.OpenRead(args[1]); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + return 1; + } + + byte[] sector = new byte[sectors]; + if (args.Length == 2) + { + uint hex; + for (int i = 0; sector_file.Position < sector_file.Length && i < 3252; i++) + { + byte[] buf = new byte[2]; + hex = uint.Parse(Encoding.ASCII.GetString(buf), NumberStyles.HexNumber); + sector[i] = (byte)hex; + } + } + else + { + sector_file.Read(sector, 0, sectors); + } + + int offset = MemSearch(sector, sync, sectors, 12); + if (offset == -1) + { + Console.WriteLine("Error searching for sync!"); + return 1; + } + + Console.WriteLine($"MSF: {sector[offset + 12]:2x}:{sector[offset + 12 + 1]:2x}:{sector[offset + 12 + 2]:2x}"); + + int shiftRegister = 0x1; + for (int i = 0; i < 3; i++) + { + sector[offset + 12 + i] ^= (byte)(shiftRegister & 0xFF); + for (int j = 0; j < 8; j++) + { + int hibit = ((shiftRegister & 1) ^ ((shiftRegister & 2) >> 1)) << 15; + shiftRegister = (hibit | shiftRegister) >> 1; + } + } + + int start_sector = (btoi(sector[offset + 12]) * 60 + btoi(sector[offset + 13])) * 75 + btoi(sector[offset + 14]) - 150; + + Console.WriteLine($"MSF: {sector[offset + 12]:2x}:{sector[offset + 12 + 1]:2x}:{sector[offset + 12 + 2]:2x}"); + + offset -= start_sector * 2352; + + Console.WriteLine($"Combined offset: {offset} bytes / {offset / 4} samples"); + return 0; + } + + /// + /// Search for a byte array in another + /// + private int MemSearch(in byte[] buf_where, in byte[] buf_search, int buf_where_len, int buf_search_len) + { + for (int i = 0; i <= buf_where_len - buf_search_len; i++) + { + for (int j = 0; j < buf_search_len; j++) + { + if (buf_where[i + j] != buf_search[j]) + break; + + if (j + 1 == buf_search_len) + return i; + } + } + + return -1; + } + } +} diff --git a/BurnOutSharp/External/psxt001z/Track.cs b/BurnOutSharp/External/psxt001z/Track.cs new file mode 100644 index 00000000..08f32984 --- /dev/null +++ b/BurnOutSharp/External/psxt001z/Track.cs @@ -0,0 +1,310 @@ +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 + } +}