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
+ }
+}