diff --git a/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj
index 6017f778f6..af11ed18f6 100644
--- a/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj
+++ b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj
@@ -74,6 +74,7 @@
+
diff --git a/BizHawk.Emulation.DiscSystem/Disc.cs b/BizHawk.Emulation.DiscSystem/Disc.cs
index 371f6d557d..ddc5557304 100644
--- a/BizHawk.Emulation.DiscSystem/Disc.cs
+++ b/BizHawk.Emulation.DiscSystem/Disc.cs
@@ -66,6 +66,11 @@ namespace BizHawk.Emulation.DiscSystem
{
public partial class Disc : IDisposable
{
+ ///
+ /// Free-form optional memos about the disc
+ ///
+ public Dictionary Memos = new Dictionary();
+
///
/// The raw TOC entries found in the lead-in track.
///
@@ -204,9 +209,19 @@ FILE ""xarp.barp.marp.farp"" BINARY
{
var ret = new Disc();
ret.FromCuePathInternal(cuePath, prefs);
+
ret.Structure.Synthesize_TOCPointsFromSessions();
ret.Synthesize_SubcodeFromStructure();
ret.Synthesize_TOCRawFromStructure();
+
+ //try loading SBI. make sure its done after the subcode is synthesized!
+ string sbiPath = Path.ChangeExtension(cuePath, "sbi");
+ if (File.Exists(sbiPath) && SBI_Format.QuickCheckISSBI(sbiPath))
+ {
+ var sbi = new SBI_Format().LoadSBIPath(sbiPath);
+ ret.ApplySBI(sbi);
+ }
+
return ret;
}
@@ -251,6 +266,33 @@ FILE ""xarp.barp.marp.farp"" BINARY
TOCRaw.LeadoutTimestamp = new Timestamp(lastEnd);
}
+ ///
+ /// applies an SBI file to the disc
+ ///
+ public void ApplySBI(SBI_Format.SBIFile sbi)
+ {
+ //save this, it's small, and we'll want it for disc processing a/b checks
+ Memos["sbi"] = sbi;
+
+ int n = sbi.ABAs.Count;
+ byte[] subcode = new byte[96];
+ int b=0;
+ for (int i = 0; i < n; i++)
+ {
+ int aba = sbi.ABAs[i];
+ var oldSubcode = this.Sectors[aba].SubcodeSector;
+ oldSubcode.ReadSubcodeDeinterleaved(subcode, 0);
+ for (int j = 0; j < 12; j++)
+ {
+ short patch = sbi.subq[b++];
+ if (patch == -1) continue;
+ else subcode[12 + j] = (byte)patch;
+ }
+ var bss = new BufferedSubcodeSector();
+ Sectors[aba].SubcodeSector = BufferedSubcodeSector.CloneFromBytesDeinterleaved(subcode);
+ }
+ }
+
///
/// Creates the subcode (really, just subchannel Q) for this disc from its current TOC.
/// Depends on the TOCPoints existing in the structure
@@ -283,10 +325,6 @@ FILE ""xarp.barp.marp.farp"" BINARY
}
var dp = Structure.Points[dpIndex];
- if (aba == 4903 + 150)
- {
- int zzz = 9;
- }
var se = Sectors[aba];
@@ -366,8 +404,14 @@ FILE ""xarp.barp.marp.farp"" BINARY
return new BCD2 {DecimalValue = d};
}
+ public static int BCDToInt(byte n)
+ {
+ var bcd = new BCD2();
+ bcd.BCDValue = n;
+ return bcd.DecimalValue;
+ }
- static byte IntToBCD(int n)
+ public static byte IntToBCD(int n)
{
int ones;
int tens = Math.DivRem(n, 10, out ones);
diff --git a/BizHawk.Emulation.DiscSystem/SBI_format.cs b/BizHawk.Emulation.DiscSystem/SBI_format.cs
new file mode 100644
index 0000000000..7f31b45765
--- /dev/null
+++ b/BizHawk.Emulation.DiscSystem/SBI_format.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Text;
+using System.IO;
+using System.Collections.Generic;
+
+using BizHawk.Common.IOExtensions;
+
+namespace BizHawk.Emulation.DiscSystem
+{
+ public class SBI_Format
+ {
+ public class SBIParseException : Exception
+ {
+ public SBIParseException(string message) : base(message) { }
+ }
+
+ public class SBIFile
+ {
+ ///
+ /// a list of patched ABAs
+ ///
+ public List ABAs = new List();
+
+ ///
+ /// 12 values (Q subchannel data) for every patched ABA; -1 means unpatched
+ ///
+ public short[] subq;
+ }
+
+ ///
+ /// Does a cursory check to see if the file looks like an SBI
+ ///
+ public static bool QuickCheckISSBI(string path)
+ {
+ using (var fs = File.OpenRead(path))
+ {
+ BinaryReader br = new BinaryReader(fs);
+ string sig = br.ReadStringFixedAscii(4);
+ if (sig != "SBI\0")
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Loads an SBI file from the specified path
+ ///
+ public SBIFile LoadSBIPath(string path)
+ {
+ using(var fs = File.OpenRead(path))
+ {
+ BinaryReader br = new BinaryReader(fs);
+ string sig = br.ReadStringFixedAscii(4);
+ if (sig != "SBI\0")
+ throw new SBIParseException("Missing magic number");
+
+ SBIFile ret = new SBIFile();
+ List bytes = new List();
+
+ //read records until done
+ for (; ; )
+ {
+ //graceful end
+ if (fs.Position == fs.Length)
+ break;
+
+ if (fs.Position+4 > fs.Length) throw new SBIParseException("Broken record");
+ var m = BCD2.BCDToInt(br.ReadByte());
+ var s = BCD2.BCDToInt(br.ReadByte());
+ var f = BCD2.BCDToInt(br.ReadByte());
+ var ts = new Timestamp(m, s, f);
+ ret.ABAs.Add(ts.Sector);
+ int type = br.ReadByte();
+ switch (type)
+ {
+ case 1: //Q0..Q9
+ if (fs.Position + 10 > fs.Length) throw new SBIParseException("Broken record");
+ for (int i = 0; i <= 9; i++) bytes.Add(br.ReadByte());
+ for (int i = 10; i <= 11; i++) bytes.Add(-1);
+ break;
+ case 2: //Q3..Q5
+ if (fs.Position + 3 > fs.Length) throw new SBIParseException("Broken record");
+ for (int i = 0; i <= 2; i++) bytes.Add(-1);
+ for (int i = 3; i <= 5; i++) bytes.Add(br.ReadByte());
+ for (int i = 6; i <= 11; i++) bytes.Add(-1);
+ break;
+ case 3: //Q7..Q9
+ if (fs.Position + 3 > fs.Length) throw new SBIParseException("Broken record");
+ for (int i = 0; i <= 6; i++) bytes.Add(-1);
+ for (int i = 7; i <= 9; i++) bytes.Add(br.ReadByte());
+ for (int i = 10; i <= 11; i++) bytes.Add(-1);
+ break;
+ default:
+ throw new SBIParseException("Broken record");
+ }
+ }
+
+ ret.subq = bytes.ToArray();
+ return ret;
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation.DiscSystem/Subcode.cs b/BizHawk.Emulation.DiscSystem/Subcode.cs
index 7488b40da8..ec69b3eeba 100644
--- a/BizHawk.Emulation.DiscSystem/Subcode.cs
+++ b/BizHawk.Emulation.DiscSystem/Subcode.cs
@@ -108,7 +108,19 @@ namespace BizHawk.Emulation.DiscSystem
Buffer.BlockCopy(SubcodeDeinterleaved, number * 12, buffer, offset, 12);
}
- public byte[] SubcodeDeinterleaved = new byte[96];
+ public BufferedSubcodeSector()
+ {
+ SubcodeDeinterleaved = new byte[96];
+ }
+
+ public static BufferedSubcodeSector CloneFromBytesDeinterleaved(byte[] buffer)
+ {
+ var ret = new BufferedSubcodeSector();
+ Buffer.BlockCopy(buffer, 0, ret.SubcodeDeinterleaved, 0, 96);
+ return ret;
+ }
+
+ public byte[] SubcodeDeinterleaved;
}
public class ZeroSubcodeSector : ISubcodeSector