diff --git a/CUETools.CDRepair/CDRepair.cs b/CUETools.CDRepair/CDRepair.cs
new file mode 100644
index 0000000..7fb88b7
--- /dev/null
+++ b/CUETools.CDRepair/CDRepair.cs
@@ -0,0 +1,499 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using CUETools.Codecs;
+using CUETools.Parity;
+
+namespace CUETools.CDRepair
+{
+ public class CDRepair : IAudioDest
+ {
+ protected int sampleCount;
+ protected int finalSampleCount;
+ protected Galois galois;
+ protected RsDecode rs;
+ protected Crc32 crc32;
+ protected uint crc;
+ protected int[] encodeGx;
+ protected int stride;
+ protected int laststride;
+ protected int stridecount;
+ protected int npar;
+
+ public CDRepair(int finalSampleCount, int stride, int npar)
+ {
+ this.npar = npar;
+ this.stride = stride;
+ this.finalSampleCount = finalSampleCount;
+ sampleCount = 0;
+ galois = Galois16.instance;
+ rs = new RsDecode16(npar, galois);
+ crc32 = new Crc32();
+ crc = 0xffffffff;
+ encodeGx = galois.makeEncodeGxLog(npar);
+ laststride = stride + (finalSampleCount * 2) % stride;
+ stridecount = (finalSampleCount * 2) / stride - 2; // minus one for leadin and one for leadout
+ if ((finalSampleCount * 2 + stride - 1) / stride + npar > galois.Max)
+ throw new Exception("invalid stride");
+ }
+
+ public CDRepair(CDRepair src)
+ : this(src.finalSampleCount, src.stride, src.npar)
+ {
+ }
+
+ public unsafe void Write(AudioBuffer sampleBuffer)
+ {
+ throw new Exception("unsupported");
+ }
+
+ public unsafe void Close()
+ {
+ if (sampleCount != finalSampleCount)
+ throw new Exception("sampleCount != finalSampleCount");
+ }
+
+ public void Delete()
+ {
+ throw new Exception("unsupported");
+ }
+
+ public int CompressionLevel
+ {
+ get { return 0; }
+ set { }
+ }
+
+ public string Options
+ {
+ set
+ {
+ if (value == null || value == "") return;
+ throw new Exception("Unsupported options " + value);
+ }
+ }
+
+ public AudioPCMConfig PCM
+ {
+ get { return AudioPCMConfig.RedBook; }
+ }
+
+ public long FinalSampleCount
+ {
+ set
+ {
+ if (value < 0) // != _toc.Length?
+ throw new Exception("invalid FinalSampleCount");
+ finalSampleCount = (int)value;
+ }
+ }
+ public long BlockSize
+ {
+ set { throw new Exception("unsupported"); }
+ }
+
+ public string Path
+ {
+ get { throw new Exception("unsupported"); }
+ }
+
+ public uint CRC
+ {
+ get
+ {
+ return crc ^ 0xffffffff;
+ }
+ }
+ }
+
+ public class CDRepairEncode : CDRepair
+ {
+ protected byte[] parity;
+ protected ushort[,] syndrome;
+ protected ushort[] leadin;
+ protected ushort[] leadout;
+ protected bool verify;
+ protected bool hasErrors = false, canRecover = true;
+ protected int actualOffset = 0;
+
+ internal int[,] sigma;
+ internal int[,] omega;
+ internal int[,] errpos;
+ internal int[,] erroff;
+ internal int[] errors;
+
+ public CDRepairEncode(int finalSampleCount, int stride, int npar, bool verify)
+ : base (finalSampleCount, stride, npar)
+ {
+ this.verify = verify;
+ parity = new byte[stride * npar * 2];
+ if (verify)
+ {
+ syndrome = new ushort[stride, npar];
+ leadin = new ushort[stride * 2];
+ leadout = new ushort[stride + laststride];
+ } else
+ syndrome = new ushort[1, npar];
+ }
+
+ new public unsafe void Write(AudioBuffer sampleBuffer)
+ {
+ sampleBuffer.Prepare(this);
+
+ if ((sampleBuffer.ByteLength & 1) != 0)
+ throw new Exception("never happens");
+
+ int firstPos = Math.Max(0, stride - sampleCount * 2);
+ int lastPos = Math.Min(sampleBuffer.ByteLength >> 1, (finalSampleCount - sampleCount) * 2 - laststride);
+
+ fixed (byte* bytes = sampleBuffer.Bytes, par = parity)
+ fixed (int* gx = encodeGx)
+ fixed (uint* t = crc32.table)
+ fixed (ushort* exp = galois.ExpTbl, log = galois.LogTbl, synptr = syndrome)
+ {
+ ushort* data = (ushort*)bytes;
+
+ if (verify)
+ for (int pos = 0; pos < (sampleBuffer.ByteLength >> 1); pos++)
+ {
+ ushort dd = data[pos];
+ if (sampleCount * 2 + pos < 2 * stride)
+ leadin[sampleCount * 2 + pos] = dd;
+ int remaining = (finalSampleCount - sampleCount) * 2 - pos - 1;
+ if (remaining < stride + laststride)
+ leadout[remaining] = dd;
+ }
+
+ if (npar == 8)
+ {
+ for (int pos = firstPos; pos < lastPos; pos++)
+ {
+ int part = (sampleCount * 2 + pos) % stride;
+ ushort* wr = ((ushort*)par) + part * 8;
+ ushort dd = data[pos];
+
+ crc = (crc >> 8) ^ t[(byte)(crc ^ dd)];
+ crc = (crc >> 8) ^ t[(byte)(crc ^ (dd >> 8))];
+
+ int ib = wr[0] ^ dd;
+ if (ib != 0)
+ {
+ ushort* myexp = exp + log[ib];
+ wr[0] = (ushort)(wr[1] ^ myexp[19483]);
+ wr[1] = (ushort)(wr[2] ^ myexp[41576]);
+ wr[2] = (ushort)(wr[3] ^ myexp[9460]);
+ wr[3] = (ushort)(wr[4] ^ myexp[52075]);
+ wr[4] = (ushort)(wr[5] ^ myexp[9467]);
+ wr[5] = (ushort)(wr[6] ^ myexp[41590]);
+ wr[6] = (ushort)(wr[7] ^ myexp[19504]);
+ wr[7] = myexp[28];
+ }
+ else
+ {
+ wr[0] = wr[1];
+ wr[1] = wr[2];
+ wr[2] = wr[3];
+ wr[3] = wr[4];
+ wr[4] = wr[5];
+ wr[5] = wr[6];
+ wr[6] = wr[7];
+ wr[7] = 0;
+ }
+
+ // syn[i] += data[pos] * α^(pos*i)
+ if (verify && dd != 0)
+ {
+ ushort* syn = synptr + part * 8;
+ ushort* myexp = exp + log[dd];
+ int offs = stridecount - (sampleCount * 2 + pos) / stride;
+ syn[0] ^= dd;
+ syn[1] ^= myexp[offs];
+ syn[2] ^= myexp[(offs * 2) % 65535];
+ syn[3] ^= myexp[(offs * 3) % 65535];
+ syn[4] ^= myexp[(offs * 4) % 65535];
+ syn[5] ^= myexp[(offs * 5) % 65535];
+ syn[6] ^= myexp[(offs * 6) % 65535];
+ syn[7] ^= myexp[(offs * 7) % 65535];
+ //ushort logdd = log[dd];
+ //syn[1] ^= exp[(logdd + offs) % 65535];
+ //syn[2] ^= exp[(logdd + offs * 2) % 65535];
+ //syn[3] ^= exp[(logdd + offs * 3) % 65535];
+ //syn[4] ^= exp[(logdd + offs * 4) % 65535];
+ //syn[5] ^= exp[(logdd + offs * 5) % 65535];
+ //syn[6] ^= exp[(logdd + offs * 6) % 65535];
+ //syn[7] ^= exp[(logdd + offs * 7) % 65535];
+ }
+ }
+ }
+ else
+ {
+ for (int pos = firstPos; pos < lastPos; pos++)
+ {
+ int part = (sampleCount * 2 + pos) % stride;
+ ushort* wr = ((ushort*)par) + part * npar;
+ ushort dd = data[pos];
+
+ crc = (crc >> 8) ^ t[(byte)(crc ^ dd)];
+ crc = (crc >> 8) ^ t[(byte)(crc ^ (dd >> 8))];
+
+ if (verify)
+ {
+ ushort* syn = synptr + part * npar;
+ syn[0] ^= dd; // wk += data
+ for (int i = 1; i < npar; i++)
+ syn[i] = (ushort)(dd ^ galois.mulExp(syn[i], i)); // wk = data + wk * α^i
+ }
+
+ int ib = wr[0] ^ dd;
+ if (ib != 0)
+ {
+ ushort* myexp = exp + log[ib];
+ for (int i = 0; i < npar - 1; i++)
+ wr[i] = (ushort)(wr[i + 1] ^ myexp[gx[i]]);
+ wr[npar - 1] = myexp[gx[npar - 1]];
+ }
+ else
+ {
+ for (int i = 0; i < npar - 1; i++)
+ wr[i] = wr[i + 1];
+ wr[npar - 1] = 0;
+ }
+ }
+ }
+ }
+ sampleCount += sampleBuffer.Length;
+ }
+
+ public unsafe bool VerifyParity(byte[] parity2)
+ {
+ return VerifyParity(parity2, 0, parity2.Length);
+ }
+
+ public unsafe bool VerifyParity(byte[] parity2, int pos, int len)
+ {
+ if (!verify)
+ throw new Exception("verify was not enabled");
+ if (sampleCount != finalSampleCount)
+ throw new Exception("sampleCount != finalSampleCount");
+ if (len != stride * npar * 2)
+ throw new Exception("wrong size");
+
+ sigma = new int[stride, npar / 2 + 2];
+ omega = new int[stride, npar / 2 + 1];
+ errpos = new int[stride, npar / 2];
+ erroff = new int[stride, npar / 2];
+ errors = new int[stride];
+
+ actualOffset = 0;
+
+ // find offset
+ fixed (byte* par2ptr = &parity2[pos])
+ {
+ ushort* par2 = (ushort*)par2ptr;
+ int* syn = stackalloc int[npar];
+ int* _sigma = stackalloc int[npar];
+ int* _omega = stackalloc int[npar];
+ int* _errpos = stackalloc int[npar];
+ int bestErrors = npar;
+
+ // We can only use offset if Abs(offset * 2) < stride,
+ // else we might need to add/remove more than one sample
+ // from syndrome calculations, and that would be too difficult
+ // and will probably require longer leadin/leadout.
+ for (int offset = 1 - stride / 2; offset < stride / 2; offset++)
+ {
+ int err = 0;
+
+ for (int i = 0; i < npar; i++)
+ {
+ int part = (stride - 1) % stride;
+ int part2 = (part + offset * 2 + stride) % stride;
+ ushort* wr = par2 + part2 * npar;
+
+ syn[i] = syndrome[part, i];
+
+ // offset < 0
+ if (part < -offset * 2)
+ {
+ syn[i] ^= galois.mulExp(leadin[stride + part], (i * (stridecount - 1)) % galois.Max);
+ syn[i] = leadout[laststride - part - 1] ^ galois.mulExp(syn[i], i);
+ }
+ // offset > 0
+ if (part >= stride - offset * 2)
+ {
+ syn[i] = galois.divExp(syn[i] ^ leadout[laststride + stride - part - 1], i);
+ syn[i] ^= galois.mulExp(leadin[part], (i * (stridecount - 1)) % galois.Max);
+ }
+
+ for (int j = 0; j < npar; j++)
+ syn[i] = wr[j] ^ galois.mulExp(syn[i], i);
+
+ err |= syn[i];
+ }
+ if (err == 0)
+ {
+ actualOffset = offset;
+ bestErrors = 0;
+ break;
+ }
+ int err_count = rs.calcSigmaMBM(_sigma, _omega, syn);
+ if (err_count > 0 && rs.chienSearch(_errpos, stridecount + npar, err_count, _sigma))
+ {
+ if (err_count < bestErrors)
+ {
+ actualOffset = offset;
+ bestErrors = err_count;
+ }
+ }
+ }
+ }
+
+ hasErrors = false;
+ fixed (byte* par = &parity2[pos])
+ fixed (ushort* exp = galois.ExpTbl, log = galois.LogTbl)
+ {
+ int* syn = stackalloc int[npar];
+ int offset = actualOffset;
+
+ for (int part = 0; part < stride; part++)
+ {
+ int part2 = (part + offset * 2 + stride) % stride;
+ ushort* wr = (ushort*)par + part2 * npar;
+ int err = 0;
+
+ for (int i = 0; i < npar; i++)
+ {
+ syn[i] = syndrome[part, i];
+
+ // offset < 0
+ if (part < -offset * 2)
+ {
+ syn[i] ^= galois.mulExp(leadin[stride + part], (i * (stridecount - 1)) % galois.Max);
+ syn[i] = leadout[laststride - part - 1] ^ galois.mulExp(syn[i], i);
+ }
+ // offset > 0
+ if (part >= stride - offset * 2)
+ {
+ syn[i] = galois.divExp(syn[i] ^ leadout[laststride + stride - part - 1], i);
+ syn[i] ^= galois.mulExp(leadin[part], (i * (stridecount - 1)) % galois.Max);
+ }
+
+ //syn[i] = galois.mulExp(syn[i], i * npar);
+
+ for (int j = 0; j < npar; j++)
+ syn[i] = wr[j] ^ galois.mulExp(syn[i], i); // wk = data + wk * α^i
+
+ err |= syn[i];
+ }
+
+ //for (int j = 0; j < npar; j++)
+ // if (wr[j] != 0)
+ // {
+ // ushort* myexp = exp + log[wr[j]];
+ // syn[0] ^= wr[j];
+ // for (int i = 1; i < npar; i++)
+ // syn[i] ^= myexp[(npar - j - 1) * i];
+ // }
+
+ //for (int i = 0; i < npar; i++)
+ // err |= syn[i];
+
+ if (err != 0)
+ {
+ hasErrors = true;
+ fixed (int* s = &sigma[part, 0], o = &omega[part, 0], e = &errpos[part, 0], f = &erroff[part, 0])
+ {
+ errors[part] = rs.calcSigmaMBM(s, o, syn);
+ if (errors[part] <= 0 || !rs.chienSearch(e, stridecount + npar, errors[part], s))
+ canRecover = false;
+ else
+ {
+ for (int i = 0; i < errors[part]; i++)
+ f[i] = galois.toPos(stridecount + npar, e[i]);
+ }
+ }
+ }
+ else
+ errors[part] = 0;
+ }
+ }
+
+ return !hasErrors;
+ }
+
+ public byte[] Parity
+ {
+ get
+ {
+ return parity;
+ }
+ }
+
+ public bool HasErrors
+ {
+ get
+ {
+ return hasErrors;
+ }
+ }
+
+ public bool CanRecover
+ {
+ get
+ {
+ return canRecover;
+ }
+ }
+
+ public int ActualOffset
+ {
+ get
+ {
+ return actualOffset;
+ }
+ }
+ }
+
+ public class CDRepairFix : CDRepair
+ {
+ CDRepairEncode decode;
+
+ public CDRepairFix(CDRepairEncode decode)
+ : base(decode)
+ {
+ this.decode = decode;
+ }
+
+ new public unsafe void Write(AudioBuffer sampleBuffer)
+ {
+ sampleBuffer.Prepare(this);
+
+ if ((sampleBuffer.ByteLength & 1) != 0)
+ throw new Exception("never happens");
+
+ int firstPos = Math.Max(0, stride - sampleCount * 2 - decode.ActualOffset * 2);
+ int lastPos = Math.Min(sampleBuffer.ByteLength >> 1, (finalSampleCount - sampleCount) * 2 - laststride - decode.ActualOffset * 2);
+
+ fixed (byte* bytes = sampleBuffer.Bytes)
+ fixed (uint* t = crc32.table)
+ {
+ ushort* data = (ushort*)bytes;
+ for (int pos = firstPos; pos < lastPos; pos++)
+ {
+ int part = (sampleCount * 2 + pos) % stride;
+ int errors = decode.errors[part];
+ fixed (int* s = &decode.sigma[part, 0], o = &decode.omega[part, 0], f = &decode.erroff[part, 0])
+ for (int i = 0; i < errors; i++)
+ if (f[i] == (sampleCount * 2 + decode.ActualOffset * 2 + pos) / stride - 1)
+ data[pos] ^= (ushort)rs.doForney(errors, decode.errpos[part, i], s, o);
+
+ ushort dd = data[pos];
+
+ crc = (crc >> 8) ^ t[(byte)(crc ^ dd)];
+ crc = (crc >> 8) ^ t[(byte)(crc ^ (dd >> 8))];
+ }
+ }
+ sampleCount += sampleBuffer.Length;
+ }
+ }
+}
diff --git a/CUETools.CDRepair/CUETools.CDRepair.csproj b/CUETools.CDRepair/CUETools.CDRepair.csproj
new file mode 100644
index 0000000..74b1385
--- /dev/null
+++ b/CUETools.CDRepair/CUETools.CDRepair.csproj
@@ -0,0 +1,62 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {C4869B37-EBB1-47BB-9406-B1209BEAB84B}
+ Library
+ Properties
+ CUETools.CDRepair
+ CUETools.CDRepair
+ v2.0
+ 512
+
+
+ true
+ full
+ false
+ ..\bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ ..\bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ {6458A13A-30EF-45A9-9D58-E5031B17BEE2}
+ CUETools.Codecs
+
+
+ {ECEB839C-171B-4535-958F-9899310A0342}
+ CUETools.Parity
+
+
+
+
+
\ No newline at end of file
diff --git a/CUETools.CDRepair/Properties/AssemblyInfo.cs b/CUETools.CDRepair/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..4e46e90
--- /dev/null
+++ b/CUETools.CDRepair/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CUETools.CDRepair")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("CUETools.CDRepair")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5e9cf235-c412-4f5f-bf0e-29acda362306")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CUETools.CTDB/CUETools.CTDB.csproj b/CUETools.CTDB/CUETools.CTDB.csproj
new file mode 100644
index 0000000..36e0677
--- /dev/null
+++ b/CUETools.CTDB/CUETools.CTDB.csproj
@@ -0,0 +1,70 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {AA2A9A7E-45FB-4632-AD85-85B0E556F818}
+ Library
+ Properties
+ CUETools.CTDB
+ CUETools.CTDB
+ v2.0
+ 512
+
+
+ true
+ full
+ false
+ ..\bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ ..\bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {1DD41038-D885-46C5-8DDE-E0B82F066584}
+ CUETools.CDImage
+
+
+ {C4869B37-EBB1-47BB-9406-B1209BEAB84B}
+ CUETools.CDRepair
+
+
+ {6458A13A-30EF-45A9-9D58-E5031B17BEE2}
+ CUETools.Codecs
+
+
+
+
+
\ No newline at end of file
diff --git a/CUETools.CTDB/CUEToolsDB.cs b/CUETools.CTDB/CUEToolsDB.cs
new file mode 100644
index 0000000..0fadd90
--- /dev/null
+++ b/CUETools.CTDB/CUEToolsDB.cs
@@ -0,0 +1,376 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text;
+using CUETools.CDImage;
+using CUETools.CDRepair;
+using Krystalware.UploadHelper;
+
+namespace CUETools.CTDB
+{
+ public class CUEToolsDB
+ {
+ private CDRepairEncode verify;
+ private CDImageLayout toc;
+ private HttpStatusCode accResult;
+ private string id;
+ private byte[] contents;
+ private int pos;
+ private int length;
+ private int confidence;
+ private int total;
+ List entries = new List();
+
+ public CUEToolsDB(CDImageLayout toc)
+ {
+ this.toc = toc;
+ this.length = (int)toc.AudioLength * 588;
+ }
+
+ public void ContactDB(string id)
+ {
+ this.id = id;
+
+ // Calculate the three disc ids used by AR
+ uint discId1 = 0;
+ uint discId2 = 0;
+ uint cddbDiscId = 0;
+
+ string[] n = id.Split('-');
+ if (n.Length != 3)
+ throw new Exception("Invalid accurateRipId.");
+ discId1 = UInt32.Parse(n[0], NumberStyles.HexNumber);
+ discId2 = UInt32.Parse(n[1], NumberStyles.HexNumber);
+ cddbDiscId = UInt32.Parse(n[2], NumberStyles.HexNumber);
+
+ string url = String.Format("http://db.cuetools.net/parity/{0:x}/{1:x}/{2:x}/dBCT-{3:d3}-{4:x8}-{5:x8}-{6:x8}.bin",
+ discId1 & 0xF, discId1 >> 4 & 0xF, discId1 >> 8 & 0xF, toc.AudioTracks, discId1, discId2, cddbDiscId);
+
+ HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
+ req.Method = "GET";
+ req.Proxy = WebRequest.GetSystemWebProxy();
+
+ try
+ {
+ HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
+ accResult = resp.StatusCode;
+
+ if (accResult == HttpStatusCode.OK)
+ {
+ using (Stream responseStream = resp.GetResponseStream())
+ {
+ using(MemoryStream memoryStream = new MemoryStream())
+ {
+ byte[] buffer = new byte[16536];
+ int count = 0;
+ do
+ {
+ count = responseStream.Read(buffer, 0, buffer.Length);
+ memoryStream.Write(buffer, 0, count);
+ } while (count != 0);
+ contents = memoryStream.ToArray();
+ }
+ }
+ }
+ Parse();
+ }
+ catch (WebException ex)
+ {
+ if (ex.Status == WebExceptionStatus.ProtocolError)
+ accResult = ((HttpWebResponse)ex.Response).StatusCode;
+ else
+ accResult = HttpStatusCode.BadRequest;
+ }
+ }
+
+ ///
+ /// Database entry format:
+ /// 'CTDB' version(0x00000100) size
+ /// 'HEAD' version(0x00000100) size disc-count total-submissions
+ /// 'DISC' version(0x00000100) size
+ /// 'TOC ' version(0x00000100) size track-count preGap
+ /// 'TRAK' ersion(0x00000100) size flags(1==isAudio) length(frames)
+ /// .... track-count
+ /// 'ARDB' version(0x00000100) size pressings-count
+ /// 'ARCD' version(0x00000100) size CRC1 ... CRCN
+ /// .... pressings-count
+ /// 'PAR8' version(0x00000100) size parity-data
+ /// 'CONF' version(0x00000100) size confidence
+ /// 'CRC ' version(0x00000100) size CRC32 min-offset max-offset ...
+ /// .... disc-count
+ ///
+ public string Submit(int confidence, int total)
+ {
+ if (id == null)
+ throw new Exception("no id");
+ // Calculate the three disc ids used by AR
+ uint discId1 = 0;
+ uint discId2 = 0;
+ uint cddbDiscId = 0;
+
+ string[] n = id.Split('-');
+ if (n.Length != 3)
+ throw new Exception("Invalid accurateRipId.");
+ discId1 = UInt32.Parse(n[0], NumberStyles.HexNumber);
+ discId2 = UInt32.Parse(n[1], NumberStyles.HexNumber);
+ cddbDiscId = UInt32.Parse(n[2], NumberStyles.HexNumber);
+
+ UploadFile[] files = new UploadFile[1];
+ MemoryStream newcontents = new MemoryStream();
+ using (DBHDR CTDB = new DBHDR(newcontents, "CTDB", 0x100))
+ {
+ using (DBHDR HEAD = new DBHDR(newcontents, "HEAD", 0x100))
+ {
+ DBHDR.WriteInt(newcontents, 1); // disc count
+ DBHDR.WriteInt(newcontents, total);
+ }
+ using (DBHDR DISC = new DBHDR(newcontents, "DISC", 0x100))
+ {
+ using (DBHDR TOC = new DBHDR(newcontents, "TOC ", 0x100))
+ {
+ DBHDR.WriteInt(newcontents, toc.TrackCount);
+ DBHDR.WriteInt(newcontents, toc.Pregap);
+ for (int i = 1; i <= toc.TrackCount; i++)
+ using (DBHDR TRAK = new DBHDR(newcontents, "TRAK", 0x100))
+ {
+ DBHDR.WriteInt(newcontents, toc[i].IsAudio ? 1 : 0);
+ DBHDR.WriteInt(newcontents, toc[i].Length);
+ }
+ }
+ using (DBHDR PAR8 = new DBHDR(newcontents, "CONF", 0x100))
+ {
+ DBHDR.WriteInt(newcontents, confidence);
+ }
+ using (DBHDR PAR8 = new DBHDR(newcontents, "CRC ", 0x100))
+ {
+ DBHDR.WriteInt(newcontents, verify.CRC);
+ }
+ using (DBHDR PAR8 = new DBHDR(newcontents, "PAR8", 0x100))
+ {
+ newcontents.Write(verify.Parity, 0, verify.Parity.Length);
+ }
+ }
+ }
+ newcontents.Position = 0;
+ files[0] = new UploadFile(newcontents, "uploadedfile", "data.bin", "image/binary");
+ HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://db.cuetools.net/uploader.php");
+ req.Proxy = WebRequest.GetSystemWebProxy();
+ NameValueCollection form = new NameValueCollection();
+ form.Add("id", String.Format("{0:d3}-{1:x8}-{2:x8}-{3:x8}", toc.AudioTracks, discId1, discId2, cddbDiscId));
+ HttpWebResponse resp = HttpUploadHelper.Upload(req, files, form);
+ string errtext;
+ using (Stream s = resp.GetResponseStream())
+ using (StreamReader sr = new StreamReader(s))
+ {
+ errtext = sr.ReadToEnd();
+ }
+ return errtext;
+ }
+
+ private string ReadHDR(out int end)
+ {
+ string res = Encoding.ASCII.GetString(contents, pos, 4);
+ pos += 4;
+ int version = ReadInt();
+ if (version >= 0x200)
+ throw new Exception("unsupported CTDB version");
+ int size = ReadInt();
+ end = pos + size;
+ return res;
+ }
+
+ private int ReadInt()
+ {
+ int value =
+ (contents[pos] +
+ (contents[pos + 1] << 8) +
+ (contents[pos + 2] << 16) +
+ (contents[pos + 3] << 24));
+ pos += 4;
+ return value;
+ }
+
+ private uint ReadUInt()
+ {
+ uint value =
+ ((uint)contents[pos] +
+ ((uint)contents[pos + 1] << 8) +
+ ((uint)contents[pos + 2] << 16) +
+ ((uint)contents[pos + 3] << 24));
+ pos += 4;
+ return value;
+ }
+
+ private void Parse()
+ {
+ if (accResult != HttpStatusCode.OK)
+ return;
+
+ pos = 0;
+ int end;
+ string hdr = ReadHDR(out end);
+ if (hdr != "CTDB") throw new Exception("invalid CTDB file");
+ if (end != contents.Length) throw new Exception("incomplete CTDB file");
+ hdr = ReadHDR(out end);
+ if (hdr != "HEAD") throw new Exception("invalid CTDB file");
+ int discCount = ReadInt();
+ total = ReadInt();
+ pos = end;
+ while (pos < contents.Length)
+ {
+ hdr = ReadHDR(out end);
+ if (hdr != "DISC")
+ {
+ pos = end;
+ continue;
+ }
+ int endDisc = end;
+ uint crc = 0;
+ int parPos = 0, parLen = 0, conf = 0;
+ while (pos < endDisc)
+ {
+ hdr = ReadHDR(out end);
+ if (hdr == "PAR8")
+ {
+ parPos = pos;
+ parLen = end - pos;
+ }
+ else if (hdr == "CRC ")
+ crc = ReadUInt();
+ else if (hdr == "CONF")
+ conf = ReadInt();
+ pos = end;
+ }
+ if (parPos != 0)
+ entries.Add(new DBEntry(parPos, parLen, conf, crc));
+ }
+ }
+
+ public void DoVerify()
+ {
+ foreach (DBEntry entry in entries)
+ {
+ verify.VerifyParity(contents, entry.pos, entry.len);
+ if (!verify.HasErrors || verify.CanRecover)
+ confidence = entry.conf;
+ break;
+ }
+ }
+
+ public void Init()
+ {
+ verify = new CDRepairEncode(length, 10 * 588 * 2, 8, accResult == HttpStatusCode.OK);
+ }
+
+ public int Total
+ {
+ get
+ {
+ return total;
+ }
+ }
+
+ public HttpStatusCode AccResult
+ {
+ get
+ {
+ return accResult;
+ }
+ }
+
+ public CDRepairEncode Verify
+ {
+ get
+ {
+ return verify;
+ }
+ }
+
+ public string DBStatus
+ {
+ get
+ {
+ return accResult == HttpStatusCode.NotFound ? "disk not present in database" :
+ accResult == HttpStatusCode.OK ? null
+ : accResult.ToString();
+ }
+ }
+
+ public string Status
+ {
+ get
+ {
+ //sw.WriteLine("CUETools DB CRC: {0:x8}", Verify.CRC);
+ if (DBStatus != null)
+ return DBStatus;
+ if (!verify.HasErrors)
+ return string.Format("verified OK, confidence {0}/{1}", confidence, total);
+ if (verify.CanRecover)
+ return string.Format("contains correctable errors, confidence {0}/{1}", confidence, total);
+ return "could not be verified";
+ }
+ }
+ }
+
+ internal class DBEntry
+ {
+ public int pos;
+ public int len;
+ public int conf;
+ public uint crc;
+
+ public DBEntry(int pos, int len, int conf, uint crc)
+ {
+ this.pos = pos;
+ this.len = len;
+ this.conf = conf;
+ this.crc = crc;
+ }
+ }
+
+ internal class DBHDR : IDisposable
+ {
+ private long lenOffs;
+ private MemoryStream stream;
+
+ public DBHDR(MemoryStream stream, string name, int version)
+ {
+ this.stream = stream;
+ stream.Write(Encoding.ASCII.GetBytes(name), 0, 4);
+ WriteInt(stream, version);
+ lenOffs = stream.Position;
+ WriteInt(stream, 0);
+ }
+
+ public void Dispose()
+ {
+ long fin = stream.Position;
+ stream.Position = lenOffs;
+ WriteInt(stream, (int)(fin - lenOffs - 4));
+ stream.Position = fin;
+ }
+
+ public static void WriteInt(MemoryStream stream, int value)
+ {
+ byte[] temp = new byte[4];
+ temp[0] = (byte)(value & 0xff);
+ temp[1] = (byte)((value >> 8) & 0xff);
+ temp[2] = (byte)((value >> 16) & 0xff);
+ temp[3] = (byte)((value >> 24) & 0xff);
+ stream.Write(temp, 0, 4);
+ }
+
+ public static void WriteInt(MemoryStream stream, uint value)
+ {
+ byte[] temp = new byte[4];
+ temp[0] = (byte)(value & 0xff);
+ temp[1] = (byte)((value >> 8) & 0xff);
+ temp[2] = (byte)((value >> 16) & 0xff);
+ temp[3] = (byte)((value >> 24) & 0xff);
+ stream.Write(temp, 0, 4);
+ }
+ }
+}
diff --git a/CUETools.CTDB/Properties/AssemblyInfo.cs b/CUETools.CTDB/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..389e16d
--- /dev/null
+++ b/CUETools.CTDB/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CUETools.CTDB")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("CUETools.CTDB")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("ac77f009-6d70-4a1c-9675-c5fec3bfe20b")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CUETools.CTDB/UploadHelper/HttpUploadHelper.cs b/CUETools.CTDB/UploadHelper/HttpUploadHelper.cs
new file mode 100644
index 0000000..aa950e7
--- /dev/null
+++ b/CUETools.CTDB/UploadHelper/HttpUploadHelper.cs
@@ -0,0 +1,114 @@
+// http://aspnetupload.com
+// Copyright © 2009 Krystalware, Inc.
+//
+// This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License
+// http://creativecommons.org/licenses/by-sa/3.0/us/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections.Specialized;
+using System.Net;
+using System.IO;
+
+namespace Krystalware.UploadHelper
+{
+ public class HttpUploadHelper
+ {
+ private HttpUploadHelper()
+ { }
+
+ public static string Upload(string url, UploadFile[] files, NameValueCollection form)
+ {
+ HttpWebResponse resp = Upload((HttpWebRequest)WebRequest.Create(url), files, form);
+
+ using (Stream s = resp.GetResponseStream())
+ using (StreamReader sr = new StreamReader(s))
+ {
+ return sr.ReadToEnd();
+ }
+ }
+
+ public static HttpWebResponse Upload(HttpWebRequest req, UploadFile[] files, NameValueCollection form)
+ {
+ List mimeParts = new List();
+
+ try
+ {
+ foreach (string key in form.AllKeys)
+ {
+ StringMimePart part = new StringMimePart();
+
+ part.Headers["Content-Disposition"] = "form-data; name=\"" + key + "\"";
+ part.StringData = form[key];
+
+ mimeParts.Add(part);
+ }
+
+ int nameIndex = 0;
+
+ foreach (UploadFile file in files)
+ {
+ StreamMimePart part = new StreamMimePart();
+
+ if (string.IsNullOrEmpty(file.FieldName))
+ file.FieldName = "file" + nameIndex++;
+
+ part.Headers["Content-Disposition"] = "form-data; name=\"" + file.FieldName + "\"; filename=\"" + file.FileName + "\"";
+ part.Headers["Content-Type"] = file.ContentType;
+
+ part.SetStream(file.Data);
+
+ mimeParts.Add(part);
+ }
+
+ string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
+
+ req.ContentType = "multipart/form-data; boundary=" + boundary;
+ req.Method = "POST";
+
+ long contentLength = 0;
+
+ byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");
+
+ foreach (MimePart part in mimeParts)
+ {
+ contentLength += part.GenerateHeaderFooterData(boundary);
+ }
+
+ req.ContentLength = contentLength + _footer.Length;
+
+ byte[] buffer = new byte[8192];
+ byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
+ int read;
+
+ using (Stream s = req.GetRequestStream())
+ {
+ foreach (MimePart part in mimeParts)
+ {
+ s.Write(part.Header, 0, part.Header.Length);
+
+ while ((read = part.Data.Read(buffer, 0, buffer.Length)) > 0)
+ s.Write(buffer, 0, read);
+
+ part.Data.Dispose();
+
+ s.Write(afterFile, 0, afterFile.Length);
+ }
+
+ s.Write(_footer, 0, _footer.Length);
+ }
+
+ return (HttpWebResponse)req.GetResponse();
+ }
+ catch
+ {
+ foreach (MimePart part in mimeParts)
+ if (part.Data != null)
+ part.Data.Dispose();
+
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CUETools.CTDB/UploadHelper/MimePart.cs b/CUETools.CTDB/UploadHelper/MimePart.cs
new file mode 100644
index 0000000..e744d12
--- /dev/null
+++ b/CUETools.CTDB/UploadHelper/MimePart.cs
@@ -0,0 +1,52 @@
+// http://aspnetupload.com
+// Copyright © 2009 Krystalware, Inc.
+//
+// This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License
+// http://creativecommons.org/licenses/by-sa/3.0/us/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.Collections.Specialized;
+
+namespace Krystalware.UploadHelper
+{
+ public abstract class MimePart
+ {
+ NameValueCollection _headers = new NameValueCollection();
+ byte[] _header;
+
+ public NameValueCollection Headers
+ {
+ get { return _headers; }
+ }
+
+ public byte[] Header
+ {
+ get { return _header; }
+ }
+
+ public long GenerateHeaderFooterData(string boundary)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append("--");
+ sb.Append(boundary);
+ sb.AppendLine();
+ foreach (string key in _headers.AllKeys)
+ {
+ sb.Append(key);
+ sb.Append(": ");
+ sb.AppendLine(_headers[key]);
+ }
+ sb.AppendLine();
+
+ _header = Encoding.UTF8.GetBytes(sb.ToString());
+
+ return _header.Length + Data.Length + 2;
+ }
+
+ public abstract Stream Data { get; }
+ }
+}
\ No newline at end of file
diff --git a/CUETools.CTDB/UploadHelper/Properties/AssemblyInfo.cs b/CUETools.CTDB/UploadHelper/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..8616d05
--- /dev/null
+++ b/CUETools.CTDB/UploadHelper/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("UploadHelper")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("UploadHelper")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("13eac2b4-c1d5-4317-a17a-5389ad4c6005")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CUETools.CTDB/UploadHelper/StreamMimePart.cs b/CUETools.CTDB/UploadHelper/StreamMimePart.cs
new file mode 100644
index 0000000..d2e765b
--- /dev/null
+++ b/CUETools.CTDB/UploadHelper/StreamMimePart.cs
@@ -0,0 +1,31 @@
+// http://aspnetupload.com
+// Copyright © 2009 Krystalware, Inc.
+//
+// This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License
+// http://creativecommons.org/licenses/by-sa/3.0/us/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace Krystalware.UploadHelper
+{
+ public class StreamMimePart : MimePart
+ {
+ Stream _data;
+
+ public void SetStream(Stream stream)
+ {
+ _data = stream;
+ }
+
+ public override Stream Data
+ {
+ get
+ {
+ return _data;
+ }
+ }
+ }
+}
diff --git a/CUETools.CTDB/UploadHelper/StringMimePart.cs b/CUETools.CTDB/UploadHelper/StringMimePart.cs
new file mode 100644
index 0000000..0304fcc
--- /dev/null
+++ b/CUETools.CTDB/UploadHelper/StringMimePart.cs
@@ -0,0 +1,34 @@
+// http://aspnetupload.com
+// Copyright © 2009 Krystalware, Inc.
+//
+// This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License
+// http://creativecommons.org/licenses/by-sa/3.0/us/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace Krystalware.UploadHelper
+{
+ public class StringMimePart : MimePart
+ {
+ Stream _data;
+
+ public string StringData
+ {
+ set
+ {
+ _data = new MemoryStream(Encoding.UTF8.GetBytes(value));
+ }
+ }
+
+ public override Stream Data
+ {
+ get
+ {
+ return _data;
+ }
+ }
+ }
+}
diff --git a/CUETools.CTDB/UploadHelper/UploadFile.cs b/CUETools.CTDB/UploadHelper/UploadFile.cs
new file mode 100644
index 0000000..27ed84b
--- /dev/null
+++ b/CUETools.CTDB/UploadHelper/UploadFile.cs
@@ -0,0 +1,61 @@
+// http://aspnetupload.com
+// Copyright © 2009 Krystalware, Inc.
+//
+// This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License
+// http://creativecommons.org/licenses/by-sa/3.0/us/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace Krystalware.UploadHelper
+{
+ public class UploadFile
+ {
+ Stream _data;
+ string _fieldName;
+ string _fileName;
+ string _contentType;
+
+ public UploadFile(Stream data, string fieldName, string fileName, string contentType)
+ {
+ _data = data;
+ _fieldName = fieldName;
+ _fileName = fileName;
+ _contentType = contentType;
+ }
+
+ public UploadFile(string fileName, string fieldName, string contentType)
+ : this(File.OpenRead(fileName), fieldName, Path.GetFileName(fileName), contentType)
+ { }
+
+ public UploadFile(string fileName)
+ : this(fileName, null, "application/octet-stream")
+ { }
+
+ public Stream Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ public string FieldName
+ {
+ get { return _fieldName; }
+ set { _fieldName = value; }
+ }
+
+ public string FileName
+ {
+ get { return _fileName; }
+ set { _fileName = value; }
+ }
+
+ public string ContentType
+ {
+ get { return _contentType; }
+ set { _contentType = value; }
+ }
+ }
+}
diff --git a/CUETools.CTDB/UploadHelper/UploadHelper.csproj b/CUETools.CTDB/UploadHelper/UploadHelper.csproj
new file mode 100644
index 0000000..025e3ae
--- /dev/null
+++ b/CUETools.CTDB/UploadHelper/UploadHelper.csproj
@@ -0,0 +1,59 @@
+
+
+ Debug
+ AnyCPU
+ 8.0.50727
+ 2.0
+ {02FBD52C-B997-4D18-9E2C-81B5AE8EFB30}
+ Library
+ Properties
+ Krystalware.UploadHelper
+ Krystalware.UploadHelper
+
+
+
+
+
+
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CUETools.CTDB/UploadHelper/uploader.php b/CUETools.CTDB/UploadHelper/uploader.php
new file mode 100644
index 0000000..d480d00
--- /dev/null
+++ b/CUETools.CTDB/UploadHelper/uploader.php
@@ -0,0 +1,77 @@
+
+ * @link http://de3.php.net/manual/en/ini.core.php#79564
+ */
+function php_to_byte($v){
+ $l = substr($v, -1);
+ $ret = substr($v, 0, -1);
+ switch(strtoupper($l)){
+ case 'P':
+ $ret *= 1024;
+ case 'T':
+ $ret *= 1024;
+ case 'G':
+ $ret *= 1024;
+ case 'M':
+ $ret *= 1024;
+ case 'K':
+ $ret *= 1024;
+ break;
+ }
+ return $ret;
+}
+
+// Return the human readable size of a file
+// @param int $size a file size
+// @param int $dec a number of decimal places
+
+function filesize_h($size, $dec = 1)
+{
+ $sizes = array('byte(s)', 'kb', 'mb', 'gb');
+ $count = count($sizes);
+ $i = 0;
+
+ while ($size >= 1024 && ($i < $count - 1)) {
+ $size /= 1024;
+ $i++;
+ }
+
+ return round($size, $dec) . ' ' . $sizes[$i];
+}
+
+
+$file = $_FILES['uploadedfile'];
+
+//echo $file['name'], ini_get('upload_max_filesize');
+
+ // give info on PHP catched upload errors
+ if($file['error']) switch($file['error']){
+ case 1:
+ case 2:
+ echo sprintf($lang['uploadsize'],
+ filesize_h(php_to_byte(ini_get('upload_max_filesize'))));
+ echo "Error ", $file['error'];
+ return;
+ default:
+ echo $lang['uploadfail'];
+ echo "Error ", $file['error'];
+ }
+
+$id = $_POST['id'];
+$err = sscanf($id, "%d-%x-%x-%x", $tracks, $id1, $id2, $cddbid);
+$target_path = sprintf("parity/%x/%x/%x", $id1 & 15, ($id1 >> 4) & 15, ($id1 >> 8) & 15);
+$target_file = sprintf("%s/dBCT-%03d-%08x-%08x-%08x.bin", $target_path, $tracks, $id1, $id2, $cddbid);
+
+@mkdir($target_path, 0777, true);
+
+if(move_uploaded_file($file['tmp_name'], $target_file)) {
+ echo "The file ". $target_file. " has been uploaded";
+} else{
+ echo "There was an error uploading the file, please try again!";
+}
+
+?>
diff --git a/CUETools.CTDB/uploader.php b/CUETools.CTDB/uploader.php
new file mode 100644
index 0000000..a8f0ed5
--- /dev/null
+++ b/CUETools.CTDB/uploader.php
@@ -0,0 +1,14 @@
+
diff --git a/CUETools.Parity/CUETools.Parity.csproj b/CUETools.Parity/CUETools.Parity.csproj
new file mode 100644
index 0000000..3257358
--- /dev/null
+++ b/CUETools.Parity/CUETools.Parity.csproj
@@ -0,0 +1,54 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {ECEB839C-171B-4535-958F-9899310A0342}
+ Library
+ Properties
+ CUETools.Parity
+ CUETools.Parity
+ v2.0
+ 512
+
+
+ true
+ full
+ false
+ ..\bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ ..\bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CUETools.Parity/Galois.cs b/CUETools.Parity/Galois.cs
new file mode 100644
index 0000000..cefad0d
Binary files /dev/null and b/CUETools.Parity/Galois.cs differ
diff --git a/CUETools.Parity/Properties/AssemblyInfo.cs b/CUETools.Parity/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..779af3a
--- /dev/null
+++ b/CUETools.Parity/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CUETools.Parity")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("CUETools.Parity")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("4310d522-9bbb-4a7f-a4e6-154bfcb8fa12")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CUETools.Parity/RsDecode.cs b/CUETools.Parity/RsDecode.cs
new file mode 100644
index 0000000..bcae922
Binary files /dev/null and b/CUETools.Parity/RsDecode.cs differ
diff --git a/CUETools.Parity/RsEncode.cs b/CUETools.Parity/RsEncode.cs
new file mode 100644
index 0000000..405c96c
Binary files /dev/null and b/CUETools.Parity/RsEncode.cs differ