public static void WriteTagToWavFile(string path, ListTag tag, int backupCount) { string bak = path + ".bak"; string tmp = path + ".tmp"; try { using (var src = new FileStream(path, FileMode.Open, FileAccess.Read)) using (var reader = new BinaryReader(src, Encoding.Default, true)) { // ファイルフォーマットの確認 var firstTag = reader.ReadUInt32(); if (firstTag != FourCC.FromString("RIFF")) { throw new FileFormatException("RIFF チャンクがありません。"); } var totalSize = reader.ReadUInt32(); if (totalSize % 2 != 0) { throw new FileFormatException("RIFF チャンクのデータ長が不正です。"); } var formType = reader.ReadUInt32(); if (formType != FourCC.FromString("WAVE")) { throw new FileFormatException("フォームタイプが WAVE ではありません。"); } using (var dest = new FileStream(tmp, FileMode.CreateNew)) using (var writer = new BinaryWriter(dest, Encoding.Default, true)) { writer.Write(FourCC.FromString("RIFF")); writer.Write((uint)0); // dummy writer.Write(FourCC.FromString("WAVE")); while (true) { long pos = src.Position; if (pos >= totalSize + sizeof(uint) * 2) { break; } var innerTag = reader.ReadUInt32(); var innerSize = reader.ReadUInt32(); // すでに存在するLISTタグ if (innerTag == FourCC.FromString("LIST")) { // データ部分は読み飛ばす src.Seek(innerSize, SeekOrigin.Current); } else { writer.Write(innerTag); writer.Write(innerSize); //src.CopyTo(dest, (int)innerSize); // CopyToだとバイト数制限が動作しない??? // 仕方ないので自前コピーする byte[] buffer = new byte[8192]; int remain = (int)innerSize; while (remain > 0) { int len = remain; if (len > buffer.Length) { len = buffer.Length; } int read = src.Read(buffer, 0, len); dest.Write(buffer, 0, read); remain -= read; } } } var tagBytes = tag.GetBytes(); // タグの書き込み writer.Write(tagBytes); // 書き込み後のカーソルから後ろを削除 dest.SetLength(dest.Position); // RIFFチャンクのサイズ情報を更新 dest.Seek(sizeof(uint), SeekOrigin.Begin); writer.Write((uint)(dest.Length - dest.Position - sizeof(uint))); } } // TODO // ここのバックアップ処理について世代管理をちゃんと行えるようにする bool replaceCompleted = false; for (int i = 0; i < backupCount; i++) { try { File.Move(path, bak); File.Move(tmp, path); if (backupCount == 0) { File.Delete(bak); } replaceCompleted = true; break; } catch (IOException) { bak = path + ".bak." + i.ToString("X2"); } } if (!replaceCompleted) { // 直接行く File.Delete(path); File.Move(tmp, path); } } catch { throw; } finally { File.Delete(tmp); } }
public byte[] GetBytes() { uint WriteChunk(BinaryWriter writer, string tag, string data) { if (data is null) { return(0); } var bytes = Encoding.Default.GetBytes(data + "\0").ToArray(); if (bytes.Length % 2 != 0) { Array.Resize(ref bytes, bytes.Length + 1); bytes[bytes.Length - 1] = 0; } writer.Write(FourCC.FromString(tag)); writer.Write((uint)bytes.Length); writer.Write(bytes); return((uint)(sizeof(uint) * 2 + bytes.Length)); } using (MemoryStream stream = new MemoryStream()) using (BinaryWriter writer = new BinaryWriter(stream, Encoding.Default, true)) { writer.Write(FourCC.FromString("LIST")); writer.Write((uint)0); // ダミー値 writer.Write(FourCC.FromString("INFO")); uint len = 0; len += WriteChunk(writer, "IART", Artist); len += WriteChunk(writer, "INAM", Name); len += WriteChunk(writer, "IPRD", Product); len += WriteChunk(writer, "IGNR", Genre); len += WriteChunk(writer, "ICMT", Comment); len += WriteChunk(writer, "ITRK", TrackNumber?.ToString()); len += WriteChunk(writer, "ICRD", CreationYear?.ToString()); len += WriteChunk(writer, "ISFT", Software); if (len == 0) { // タグ情報なし : データを返さない return(new byte[0]); } // INFO分を加算 len += sizeof(uint); if (len % 2 != 0) { len++; writer.Write((byte)0); } // サイズ部分を更新 stream.Seek(sizeof(uint), SeekOrigin.Begin); writer.Write(len); return(stream.ToArray()); } }