private int DecodeToWaveR32(byte[] blockData) { var hcaInfo = HcaInfo; if (blockData == null) { throw new ArgumentNullException(nameof(blockData)); } if (blockData.Length < hcaInfo.BlockSize) { throw new HcaException(ErrorMessages.GetInvalidParameter("blockData.Length"), ActionResult.InvalidParameter); } var checksum = HcaHelper.Checksum(blockData, 0); if (checksum != 0) { throw new HcaException(ErrorMessages.GetChecksumNotMatch(0, checksum), ActionResult.ChecksumNotMatch); } _cipher.Decrypt(blockData); var d = new DataBits(blockData, hcaInfo.BlockSize); var magic = d.GetBit(16); if (magic != 0xffff) { throw new HcaException(ErrorMessages.GetMagicNotMatch(0xffff, magic), ActionResult.MagicNotMatch); } var a = (d.GetBit(9) << 8) - d.GetBit(7); var channels = _channels; var ath = _ath; try { for (var i = 0; i < hcaInfo.ChannelCount; ++i) { channels[i].Decode1(d, hcaInfo.CompR09, a, ath.Table); } for (var i = 0; i < 8; ++i) { for (var j = 0; j < hcaInfo.ChannelCount; ++j) { channels[j].Decode2(d); } for (var j = 0; j < hcaInfo.ChannelCount; ++j) { channels[j].Decode3(hcaInfo.CompR09, hcaInfo.CompR08, (uint)(hcaInfo.CompR07 + hcaInfo.CompR06), hcaInfo.CompR05); } for (var j = 0; j < hcaInfo.ChannelCount - 1; ++j) { Channel.Decode4(ref channels[j], ref channels[j + 1], i, (uint)(hcaInfo.CompR05 - hcaInfo.CompR06), hcaInfo.CompR06, hcaInfo.CompR07); } for (var j = 0; j < hcaInfo.ChannelCount; ++j) { channels[j].Decode5(i); } } return(blockData.Length); } catch (IndexOutOfRangeException ex) { const string message = "Index access exception detected. It is probably because you are using an incorrect HCA key pair while decoding a type 56 HCA file."; throw new HcaException(message, ActionResult.DecodeFailed, ex); } }
private void TransformWaveDataBlocks(Stream source, byte[] destination, uint startBlockIndex, uint blockCount, IWaveWriter waveWriter) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (destination == null) { throw new ArgumentNullException(nameof(destination)); } if (waveWriter == null) { throw new ArgumentNullException(nameof(waveWriter)); } var hcaInfo = HcaInfo; var startOffset = hcaInfo.DataOffset + startBlockIndex * hcaInfo.BlockSize; source.Seek(startOffset, SeekOrigin.Begin); var channels = _channels; var decodeParams = _decodeParams; var hcaBlockBuffer = GetHcaBlockBuffer(); var channelCount = hcaInfo.ChannelCount; var rvaVolume = hcaInfo.RvaVolume; var bytesPerSample = waveWriter.BytesPerSample; var volume = decodeParams.Volume; for (var l = 0; l < (int)blockCount; ++l) { source.Read(hcaBlockBuffer, 0, hcaBlockBuffer.Length); DecodeToWaveR32(hcaBlockBuffer, l + (int)startBlockIndex); for (var i = 0; i < 8; ++i) { for (var j = 0; j < 0x80; ++j) { for (var k = 0; k < channelCount; ++k) { float f; unsafe { var pWave = channels.GetPtrOfWave(k); f = pWave[i * 0x80 + j]; f = f * volume * rvaVolume; } HcaHelper.Clamp(ref f, -1f, 1f); var offset = (((l * 8 + i) * 0x80 + j) * channelCount + k) * bytesPerSample; waveWriter.DecodeToBuffer(f, destination, (uint)offset); } } } } }
public HcaDecoder(Stream sourceStream, DecodeParams decodeParams) : base(sourceStream) { _decodeParams = decodeParams; HcaHelper.TranslateTables(); _ath = new Ath(); _cipher = new Cipher(); Initialize(); }
private static void FixDataBlock(byte[] blockData) { var length = blockData.Length; var sum = HcaHelper.Checksum(blockData, 0, length - 2); if (BitConverter.IsLittleEndian) { sum = DereToreHelper.SwapEndian(sum); } var sumBytes = BitConverter.GetBytes(sum); blockData[length - 2] = sumBytes[0]; blockData[length - 1] = sumBytes[1]; }
private void TransformWaveDataBlocks(Stream source, byte[] destination, uint startBlockIndex, uint blockCount, IWaveWriter waveWriter) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (destination == null) { throw new ArgumentNullException(nameof(destination)); } if (waveWriter == null) { throw new ArgumentNullException(nameof(waveWriter)); } var hcaInfo = HcaInfo; var startOffset = hcaInfo.DataOffset + startBlockIndex * hcaInfo.BlockSize; source.Seek(startOffset, SeekOrigin.Begin); var channels = _channels; var decodeParams = _decodeParams; var hcaBlockBuffer = GetHcaBlockBuffer(); var channelCount = (int)hcaInfo.ChannelCount; var rvaVolume = hcaInfo.RvaVolume; var bytesPerSample = waveWriter.BytesPerSample; foreach (var l in Enumerable.Range(0, (int)blockCount)) { source.Read(hcaBlockBuffer, 0, hcaBlockBuffer.Length); DecodeToWaveR32(hcaBlockBuffer); Parallel.For(0, 8, i => { for (var j = 0; j < 0x80; ++j) { for (var k = 0; k < channelCount; ++k) { var f = channels[k].Wave[i * 0x80 + j] * decodeParams.Volume * rvaVolume; HcaHelper.Clamp(ref f, -1f, 1f); var offset = (((l * 8 + i) * 0x80 + j) * channelCount + k) * bytesPerSample; waveWriter.DecodeToBuffer(f, destination, (uint)offset); } } }); } }
private bool ConvertBlock(byte[] blockData, Stream outputStream) { var checksum = HcaHelper.Checksum(blockData, 0); if (checksum != 0) { return(false); } _cipherFrom.Decrypt(blockData); var dataClass = new DataBits(blockData, (uint)blockData.Length); var magic = dataClass.GetBit(16); if (magic != 0xffff) { return(false); } _cipherTo.Encrypt(blockData); FixDataBlock(blockData); outputStream.Write(blockData, 0, blockData.Length); return(true); }
public void Decode5(int index) { float[] s; float[] d; s = Block; d = Wav1; int sIndex = 0, dIndex = 0; int s1Index, s2Index; for (int i = 0, count1 = 1, count2 = 0x40; i < 7; ++i, count1 <<= 1, count2 >>= 1) { int dIndex1 = dIndex, dIndex2 = dIndex + count2; for (int j = 0; j < count1; ++j) { for (int k = 0; k < count2; ++k) { float a = s[sIndex++]; float b = s[sIndex++]; d[dIndex1++] = b + a; d[dIndex2++] = a - b; } dIndex1 += count2; dIndex2 += count2; } sIndex -= 0x80; HcaHelper.Exchange(ref sIndex, ref dIndex); HcaHelper.Exchange(ref s, ref d); } s = Wav1; d = Block; sIndex = dIndex = 0; for (int i = 0, count1 = 0x40, count2 = 1; i < 7; ++i, count1 >>= 1, count2 <<= 1) { // The original array is a 2-rank array, [7][0x40]. int list1FloatIndex = i * 0x40; // The original array is a 2-rank array, [7][0x40]. int list2FloatIndex = i * 0x40; s1Index = sIndex; s2Index = sIndex + count2; int dIndex1 = dIndex; int dIndex2 = dIndex + count2 * 2 - 1; for (int j = 0; j < count1; ++j) { for (int k = 0; k < count2; ++k) { float fa = s[s1Index++]; float fb = s[s2Index++]; float fc = ChannelTables.Decode5List1Single[list1FloatIndex++]; float fd = ChannelTables.Decode5List2Single[list2FloatIndex++]; d[dIndex1++] = fa * fc - fb * fd; d[dIndex2--] = fa * fd + fb * fc; } s1Index += count2; s2Index += count2; dIndex1 += count2; dIndex2 += count2 * 3; } HcaHelper.Exchange(ref sIndex, ref dIndex); HcaHelper.Exchange(ref s, ref d); } d = Wav2; for (int i = 0; i < 0x80; ++i) { d[i] = s[i]; } s = ChannelTables.Decode5List3Single; sIndex = 0; d = Wave; // The original array is [8][0x80]. dIndex = index * 0x80; float[] s1 = Wav2; s1Index = 0x40; float[] s2 = Wav3; s2Index = 0; for (int i = 0; i < 0x40; ++i) { d[dIndex++] = s1[s1Index++] * s[sIndex++] + s2[s2Index++]; } for (int i = 0; i < 0x40; ++i) { d[dIndex++] = s[sIndex++] * s1[--s1Index] - s2[s2Index++]; } s1 = Wav2; s2 = Wav3; s1Index = 0x40 - 1; s2Index = 0; for (int i = 0; i < 0x40; ++i) { s2[s2Index++] = s1[s1Index--] * s[--sIndex]; } for (int i = 0; i < 0x40; ++i) { s2[s2Index++] = s[--sIndex] * s1[++s1Index]; } }
private int DecodeToWaveR32(byte[] blockData, int blockIndex) { var hcaInfo = HcaInfo; if (blockData == null) { throw new ArgumentNullException(nameof(blockData)); } if (blockData.Length < hcaInfo.BlockSize) { throw new HcaException(ErrorMessages.GetInvalidParameter(nameof(blockData) + "." + nameof(blockData.Length)), ActionResult.InvalidParameter); } var checksum = HcaHelper.Checksum(blockData, 0); if (checksum != 0) { throw new HcaException(ErrorMessages.GetChecksumNotMatch(0, checksum), ActionResult.ChecksumNotMatch); } _cipher.Decrypt(blockData); var d = new DataBits(blockData, hcaInfo.BlockSize); var magic = d.GetBit(16); if (magic != 0xffff) { throw new HcaException(ErrorMessages.GetMagicNotMatch(0xffff, magic), ActionResult.MagicNotMatch); } var a = (d.GetBit(9) << 8) - d.GetBit(7); var channels = _channels; var ath = _ath; string site = null; try { int i; for (i = 0; i < hcaInfo.ChannelCount; ++i) { site = $"Decode1({i.ToString()})"; channels.Decode1(i, d, hcaInfo.CompR09, a, ath.Table); } for (i = 0; i < 8; ++i) { for (var j = 0; j < hcaInfo.ChannelCount; ++j) { site = $"Decode2({i.ToString()}/{j.ToString()})"; channels.Decode2(j, d); } for (var j = 0; j < hcaInfo.ChannelCount; ++j) { site = $"Decode3({i.ToString()}/{j.ToString()})"; channels.Decode3(j, hcaInfo.CompR09, hcaInfo.CompR08, (uint)(hcaInfo.CompR07 + hcaInfo.CompR06), hcaInfo.CompR05); } for (var j = 0; j < hcaInfo.ChannelCount - 1; ++j) { site = $"Decode4({i.ToString()}/{j.ToString()})"; channels.Decode4(j, j + 1, i, (uint)(hcaInfo.CompR05 - hcaInfo.CompR06), hcaInfo.CompR06, hcaInfo.CompR07); } for (var j = 0; j < hcaInfo.ChannelCount; ++j) { site = $"Decode5({i.ToString()}/{j.ToString()})"; channels.Decode5(j, i); } } return(blockData.Length); } catch (IndexOutOfRangeException ex) { const string message = "Index access exception detected. It is probably because you are using an incorrect HCA key pair while decoding a type 56 HCA file."; var siteInfo = $"Site: {site} @ block {blockIndex.ToString()}"; var err = message + Environment.NewLine + siteInfo; throw new HcaException(err, ActionResult.DecodeFailed, ex); } }
internal void ParseHeaders() { var stream = SourceStream; uint v; var hcaInfo = new HcaInfo(); // HCA v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.HCA)) { HcaHeader header; stream.Read(out header); hcaInfo.Version = DereToreHelper.SwapEndian(header.Version); hcaInfo.DataOffset = DereToreHelper.SwapEndian(header.DataOffset); } else { throw new HcaException("Missing HCA signature.", ActionResult.MagicNotMatch); } // FMT v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.FMT)) { FormatHeader header; stream.Read(out header); hcaInfo.ChannelCount = header.Channels; hcaInfo.SamplingRate = DereToreHelper.SwapEndian(header.SamplingRate << 8); hcaInfo.BlockCount = DereToreHelper.SwapEndian(header.Blocks); hcaInfo.FmtR01 = DereToreHelper.SwapEndian(header.R01); hcaInfo.FmtR02 = DereToreHelper.SwapEndian(header.R02); if (hcaInfo.ChannelCount < 1 || hcaInfo.ChannelCount > 16) { throw new HcaException($"Channel count should be between 1 and 16, read {hcaInfo.ChannelCount}.", ActionResult.InvalidFieldValue); } if (hcaInfo.SamplingRate < 1 || hcaInfo.SamplingRate > 0x7fffff) { throw new HcaException($"Sampling rate should be between 1 and {0x7fffffff}, read {hcaInfo.SamplingRate}.", ActionResult.InvalidFieldValue); } } else { throw new HcaException("Missing FMT signature.", ActionResult.MagicNotMatch); } // COMP / DEC v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.COMP)) { CompressHeader header; stream.Read(out header); hcaInfo.BlockSize = DereToreHelper.SwapEndian(header.BlockSize); hcaInfo.CompR01 = header.R01; hcaInfo.CompR02 = header.R02; hcaInfo.CompR03 = header.R03; hcaInfo.CompR04 = header.R04; hcaInfo.CompR05 = header.R05; hcaInfo.CompR06 = header.R06; hcaInfo.CompR07 = header.R07; hcaInfo.CompR08 = header.R08; if ((hcaInfo.BlockSize < 8 || hcaInfo.BlockSize > 0xffff) && hcaInfo.BlockSize != 0) { throw new HcaException($"Block size should be between 8 and {0xffff}, read {hcaInfo.BlockSize}.", ActionResult.InvalidFieldValue); } if (!(hcaInfo.CompR01 <= hcaInfo.CompR02 && hcaInfo.CompR02 <= 0x1f)) { throw new HcaException($"CompR01 should be less than or equal to CompR02, and CompR02 should be less than or equal to {0x1f}, read {hcaInfo.CompR01} and {hcaInfo.CompR02}.", ActionResult.InvalidFieldValue); } } else if (MagicValues.IsMagicMatch(v, MagicValues.DEC)) { DecodeHeader header; stream.Read(out header); hcaInfo.CompR01 = header.R01; hcaInfo.CompR02 = header.R02; hcaInfo.CompR03 = header.R04; hcaInfo.CompR04 = header.R03; hcaInfo.CompR05 = (ushort)(header.Count1 + 1); hcaInfo.CompR06 = (ushort)((header.EnableCount2 ? header.Count2 : header.Count1) + 1); hcaInfo.CompR07 = (ushort)(hcaInfo.CompR05 - hcaInfo.CompR06); hcaInfo.CompR08 = 0; if ((hcaInfo.BlockSize < 8 || hcaInfo.BlockSize > 0xffff) && hcaInfo.BlockSize != 0) { throw new HcaException($"Block size should be between 8 and {0xffff}, read {hcaInfo.BlockSize}.", ActionResult.InvalidFieldValue); } if (!(hcaInfo.CompR01 <= hcaInfo.CompR02 && hcaInfo.CompR02 <= 0x1f)) { throw new HcaException($"CompR01 should be less than or equal to CompR02, and CompR02 should be less than or equal to {0x1f}, read {hcaInfo.CompR01} and {hcaInfo.CompR02}.", ActionResult.InvalidFieldValue); } if (hcaInfo.CompR03 == 0) { hcaInfo.CompR03 = 1; } } else { throw new HcaException("Missing COMP/DEC signature.", ActionResult.MagicNotMatch); } // VBR v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.VBR)) { VbrHeader header; stream.Read(out header); hcaInfo.VbrR01 = DereToreHelper.SwapEndian(header.R01); hcaInfo.VbrR02 = DereToreHelper.SwapEndian(header.R02); if (!(hcaInfo.BlockSize == 0 && hcaInfo.VbrR01 < 0x01ff)) { throw new HcaException($"VbrR01 should be less than {0x01ff} in VBR HCA.", ActionResult.InvalidFieldValue); } } else { hcaInfo.VbrR01 = hcaInfo.VbrR02 = 0; } // ATH v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.ATH)) { AthHeader header; stream.Read(out header); hcaInfo.AthType = header.Type; } else { hcaInfo.AthType = (ushort)(hcaInfo.Version < 0x0200 ? 1 : 0); } // LOOP v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.LOOP)) { LoopHeader header; stream.Read(out header); hcaInfo.LoopStart = DereToreHelper.SwapEndian(header.LoopStart); hcaInfo.LoopEnd = DereToreHelper.SwapEndian(header.LoopEnd); hcaInfo.LoopR01 = DereToreHelper.SwapEndian(header.R01); hcaInfo.LoopR02 = DereToreHelper.SwapEndian(header.R02); hcaInfo.LoopFlag = true; } else { hcaInfo.LoopStart = 0; hcaInfo.LoopEnd = 0; hcaInfo.LoopR01 = 0; hcaInfo.LoopR02 = 0x400; hcaInfo.LoopFlag = false; } // CIPH v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.CIPH)) { CipherHeader header; stream.Read(out header); hcaInfo.CipherType = (CipherType)DereToreHelper.SwapEndian(header.Type); } else { hcaInfo.CipherType = CipherType.NoChipher; } // RVA v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.RVA)) { RvaHeader header; stream.Read(out header); hcaInfo.RvaVolume = DereToreHelper.SwapEndian(header.Volume); } else { hcaInfo.RvaVolume = 1; } // COMM v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.COMM)) { CommentHeader header; stream.Read(out header); hcaInfo.CommentLength = header.Length; var tmpCommentCharList = new List <byte>(); byte tmpByte; do { tmpByte = (byte)stream.ReadByte(); tmpCommentCharList.Add(tmpByte); } while (tmpByte != 0); hcaInfo.Comment = tmpCommentCharList.ToArray(); } else { hcaInfo.CommentLength = 0; hcaInfo.Comment = null; } // PAD (undocumented) v = stream.PeekUInt32LE(); if (MagicValues.IsMagicMatch(v, MagicValues.PAD)) { stream.Skip(4); // Length of 'pad ' } if (hcaInfo.CompR03 == 0) { hcaInfo.CompR03 = 1; } if (hcaInfo.CompR01 != 1 || hcaInfo.CompR02 != 0xf) { throw new HcaException($"Expected CompR01=1, CompR02=15, read {hcaInfo.CompR01}, {hcaInfo.CompR02}.", ActionResult.InvalidFieldValue); } hcaInfo.CompR09 = HcaHelper.Ceil2((uint)(hcaInfo.CompR05 - (hcaInfo.CompR06 + hcaInfo.CompR07)), hcaInfo.CompR08); HcaInfo = hcaInfo; }