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