Imported more DataFormats from original ExtensibleDataStorage codebase

This commit is contained in:
Michael Becker 2019-09-19 08:09:18 -04:00
parent d87b7580de
commit 2e89b9e13c
14 changed files with 1216 additions and 0 deletions

View File

@ -0,0 +1,55 @@
//
// INTXLINKDataFormat.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using UniversalEditor.IO;
using UniversalEditor.ObjectModels.Shortcut;
namespace UniversalEditor.DataFormats.Shortcut.Linux
{
public class LinuxShortcutDataFormat : DataFormat
{
protected override void LoadInternal(ref ObjectModel objectModel)
{
ShortcutObjectModel shortcut = (objectModel as ShortcutObjectModel);
if (shortcut == null)
throw new ObjectModelNotSupportedException();
Reader br = base.Accessor.Reader;
string INTXLNK = br.ReadFixedLengthString(7);
if (INTXLNK != "INTXLNK")
throw new InvalidDataFormatException();
byte b = br.ReadByte();
shortcut.ExecutableFileName = br.ReadStringToEnd(Encoding.UTF16LittleEndian);
}
protected override void SaveInternal(ObjectModel objectModel)
{
ShortcutObjectModel shortcut = (objectModel as ShortcutObjectModel);
if (shortcut == null)
throw new ObjectModelNotSupportedException();
Writer bw = base.Accessor.Writer;
bw.WriteFixedLengthString("INTXLNK");
bw.WriteByte(1);
bw.WriteFixedLengthString(shortcut.ExecutableFileName, Encoding.UTF16LittleEndian);
}
}
}

View File

@ -179,6 +179,7 @@
<Compile Include="ObjectModels\JSON\Fields\JSONObjectField.cs" />
<Compile Include="ObjectModels\JSON\Fields\JSONStringField.cs" />
<Compile Include="DataFormats\Text\Plain\PlainTextDataFormat.cs" />
<Compile Include="DataFormats\Shortcut\Linux\LinuxShortcutDataFormat.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UniversalEditor.Core\UniversalEditor.Core.csproj">
@ -199,6 +200,7 @@
<Folder Include="ObjectModels\JSON\" />
<Folder Include="ObjectModels\JSON\Fields\" />
<Folder Include="DataFormats\Text\Plain\" />
<Folder Include="DataFormats\Shortcut\Linux\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -0,0 +1,96 @@
//
// HADataFormat.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using UniversalEditor.IO;
using UniversalEditor.ObjectModels.FileSystem;
namespace UniversalEditor.DataFormats.FileSystem.HA
{
public class HADataFormat : DataFormat
{
public Compression.CompressionMethod CompressionMethod { get; set; } = Compression.CompressionMethod.None;
protected override DataFormatReference MakeReferenceInternal()
{
return base.MakeReferenceInternal();
}
protected override void LoadInternal(ref ObjectModel objectModel)
{
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
if (fsom == null)
throw new ObjectModelNotSupportedException();
Reader br = base.Accessor.Reader;
string HA = br.ReadFixedLengthString(2);
short cnt = br.ReadInt16();
for (short c = 0; c < cnt; c++)
{
byte versionAndType = br.ReadByte();
int compressedSize = br.ReadInt32();
int decompressedSize = br.ReadInt32();
int crc = br.ReadInt32();
int fileTime = br.ReadInt32();
string path = br.ReadNullTerminatedString();
string name = br.ReadNullTerminatedString();
string fileName = path + System.IO.Path.DirectorySeparatorChar.ToString() + name;
byte machineSpecificInformationLength = br.ReadByte();
for (byte b = 0; b < machineSpecificInformationLength; b = (byte)(b + 1))
{
byte machineSpecificInformationType = br.ReadByte();
byte machineSpecificInformation = br.ReadByte();
}
File f = fsom.AddFile(fileName);
}
}
protected override void SaveInternal(ObjectModel objectModel)
{
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
if (fsom == null)
throw new ObjectModelNotSupportedException();
Writer bw = base.Accessor.Writer;
bw.WriteFixedLengthString("HA");
File[] files = fsom.GetAllFiles();
bw.WriteInt16((short)files.Length);
foreach (File f in files)
{
byte versionAndType = 0;
bw.WriteByte(versionAndType);
byte[] decompressedData = f.GetData();
byte[] compressedData = Compression.CompressionModule.FromKnownCompressionMethod(CompressionMethod).Compress(decompressedData);
bw.WriteInt32(compressedData.Length);
bw.WriteInt32(decompressedData.Length);
int crc = 0;
bw.WriteInt32(crc);
int filetime = 0;
bw.WriteInt32(filetime);
string path = System.IO.Path.GetDirectoryName(f.Name);
string name = System.IO.Path.GetFileName(f.Name);
bw.WriteNullTerminatedString(path);
bw.WriteNullTerminatedString(name);
byte machineSpecificInformationLength = 0;
bw.WriteByte(machineSpecificInformationLength);
}
}
}
}

View File

@ -0,0 +1,210 @@
//
// HyperArchiverDataFormat.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using UniversalEditor.IO;
using UniversalEditor.ObjectModels.FileSystem;
using UniversalEditor.ObjectModels.FileSystem.FileSources;
namespace UniversalEditor.DataFormats.FileSystem.HyperArchiver
{
public class HyperArchiverDataFormat : DataFormat
{
protected override void LoadInternal(ref ObjectModel objectModel)
{
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
if (fsom == null)
throw new ObjectModelNotSupportedException();
Reader br = base.Accessor.Reader;
if (br.ReadByte() != 0x1a)
throw new InvalidDataFormatException();
while (!br.EndOfStream)
{
Compression.CompressionMethod CompressionMethod = Compression.CompressionMethod.None;
switch (br.ReadFixedLengthString(2))
{
case "HP":
{
CompressionMethod = Compression.CompressionMethod.LZW;
break;
}
case "ST":
{
CompressionMethod = Compression.CompressionMethod.None;
break;
}
case "BZ":
{
CompressionMethod = Compression.CompressionMethod.Bzip2;
break;
}
case "DE":
{
CompressionMethod = Compression.CompressionMethod.Deflate;
break;
}
case "DZ":
{
CompressionMethod = Compression.CompressionMethod.Deflate64;
break;
}
case "GZ":
{
CompressionMethod = Compression.CompressionMethod.Gzip;
break;
}
case "MA":
{
CompressionMethod = Compression.CompressionMethod.LZMA;
break;
}
case "ZS":
{
CompressionMethod = Compression.CompressionMethod.LZSS;
break;
}
case "PP":
{
CompressionMethod = Compression.CompressionMethod.PPPMd;
break;
}
}
byte version = br.ReadByte();
int compressedFileSize = br.ReadInt32();
uint decompressedFileSize = br.ReadUInt32();
int dateTimeMSDOS = br.ReadInt32();
int crc32 = br.ReadInt32();
byte fileAttribute = br.ReadByte();
byte fileNameLength = br.ReadByte();
string fileName = br.ReadFixedLengthString(fileNameLength);
File f = fsom.AddFile(fileName);
f.DataRequest += F_DataRequest;
f.Properties.Add("reader", br);
f.Properties.Add("offset", base.Accessor.Position);
f.Properties.Add("method", CompressionMethod);
f.Properties.Add("compressedLength", compressedFileSize);
f.Properties.Add("decompressedLength", decompressedFileSize);
br.Seek(compressedFileSize, SeekOrigin.Current);
}
}
void F_DataRequest(object sender, DataRequestEventArgs e)
{
File f = (sender as File);
Reader br = (Reader)f.Properties["reader"];
Compression.CompressionMethod CompressionMethod = (Compression.CompressionMethod)f.Properties["method"];
int compressedLength = (int)f.Properties["compressedLength"];
uint decompressedLength = (uint)f.Properties["decompressedLength"];
byte[] compressedData = br.ReadBytes(compressedLength);
byte[] decompressedData = Compression.CompressionModule.FromKnownCompressionMethod(CompressionMethod).Decompress(compressedData); //, decompressedLength);
e.Data = decompressedData;
}
public byte Version { get; set; } = 0;
protected override void SaveInternal(ObjectModel objectModel)
{
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
if (fsom == null)
throw new ObjectModelNotSupportedException();
Writer bw = base.Accessor.Writer;
bw.WriteByte(0x1a);
File[] files = fsom.GetAllFiles();
for (int i = 0; i < files.Length; i++)
{
Compression.CompressionMethod compressionMethod = Compression.CompressionMethod.None; // files[i].CompressionMethod;
switch (compressionMethod)
{
case Compression.CompressionMethod.Deflate:
{
bw.WriteFixedLengthString("DE");
break;
}
case Compression.CompressionMethod.Deflate64:
{
bw.WriteFixedLengthString("DZ");
break;
}
case Compression.CompressionMethod.Bzip2:
{
bw.WriteFixedLengthString("BZ");
break;
}
case Compression.CompressionMethod.LZMA:
{
bw.WriteFixedLengthString("MA");
break;
}
case Compression.CompressionMethod.LZW:
{
bw.WriteFixedLengthString("HP");
break;
}
case Compression.CompressionMethod.LZSS:
{
bw.WriteFixedLengthString("ZS");
break;
}
case Compression.CompressionMethod.PPPMd:
{
bw.WriteFixedLengthString("PP");
break;
}
case Compression.CompressionMethod.Gzip:
{
bw.WriteFixedLengthString("GZ");
break;
}
default:
{
bw.WriteFixedLengthString("ST");
break;
}
}
bw.WriteByte(Version);
byte[] decompressedData = files[i].GetData();
byte[] compressedData = Compression.CompressionModule.FromKnownCompressionMethod(compressionMethod).Compress(decompressedData);
int compressedFileSize = compressedData.Length;
uint decompressedFileSize = (uint)decompressedData.Length;
bw.WriteInt32(compressedFileSize);
bw.WriteUInt32(decompressedFileSize);
int dateTimeMSDOS = 0;
bw.WriteInt32(dateTimeMSDOS);
int crc32 = 0;
bw.WriteInt32(crc32);
byte fileAttribute = 0;
bw.WriteByte(fileAttribute);
byte fileNameLength = (byte)files[i].Name.Length;
bw.WriteByte(fileNameLength);
bw.WriteFixedLengthString(files[i].Name, fileNameLength);
bw.WriteBytes(compressedData);
}
}
}
}

View File

@ -228,6 +228,8 @@
<Compile Include="DataFormats\FileSystem\VMware\VMDKAdapterType.cs" />
<Compile Include="DataFormats\FileSystem\VMware\VMDKCreateType.cs" />
<Compile Include="DataFormats\FileSystem\VMware\VMDKDiskGeometry.cs" />
<Compile Include="DataFormats\FileSystem\HA\HADataFormat.cs" />
<Compile Include="DataFormats\FileSystem\HyperArchiver\HyperArchiverDataFormat.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Libraries\UniversalEditor.Core\UniversalEditor.Core.csproj">
@ -263,6 +265,8 @@
<Folder Include="DataFormats\FileSystem\SquareSoft\" />
<Folder Include="DataFormats\FileSystem\RealNetworks\" />
<Folder Include="DataFormats\FileSystem\VMware\" />
<Folder Include="DataFormats\FileSystem\HA\" />
<Folder Include="DataFormats\FileSystem\HyperArchiver\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -0,0 +1,29 @@
//
// AdlibBlockType.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.EdLib
{
public enum AdlibBlockType
{
Unknown = -1,
MusicData = 0
}
}

View File

@ -0,0 +1,29 @@
//
// AdlibSoundcard.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.EdLib
{
public enum AdlibSoundcard
{
Unknown = -1,
Adlib = 0
}
}

View File

@ -0,0 +1,155 @@
//
// D00DataFormat.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using UniversalEditor.IO;
namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.EdLib
{
public class D00DataFormat : DataFormat
{
private AdlibBlockType mvarBlockType = AdlibBlockType.MusicData;
public AdlibBlockType BlockType { get { return mvarBlockType; } set { mvarBlockType = value; } }
private byte mvarPlayerVersion = 0x04;
public byte PlayerVersion { get { return mvarPlayerVersion; } set { mvarPlayerVersion = value; } }
private byte mvarTimerSpeed = 0x46;
public byte TimerSpeed { get { return mvarTimerSpeed; } set { mvarTimerSpeed = value; } }
private AdlibSoundcard mvarSoundcard = AdlibSoundcard.Adlib;
public AdlibSoundcard Soundcard { get { return mvarSoundcard; } set { mvarSoundcard = value; } }
private string mvarSongTitle = "";
public string SongTitle { get { return mvarSongTitle; } set { mvarSongTitle = value; } }
private string mvarSongArtist = "";
public string SongArtist { get { return mvarSongArtist; } set { mvarSongArtist = value; } }
private byte[] mvarReserved = new byte[32];
public byte[] Reserved { get { return mvarReserved; } set { mvarReserved = value; } }
protected override void LoadInternal(ref ObjectModel objectModel)
{
Reader br = base.Accessor.Reader;
byte[] magic = br.ReadBytes(6); // 'J', 'C', 'H', 0x26, 0x02, 0x66
if (!magic.Match(new byte[] { (byte)'J', (byte)'C', (byte)'H', 0x26, 0x02, 0x66 }))
throw new InvalidDataFormatException();
byte blockType = br.ReadByte(); // 0x00 for music data
if (blockType == 0)
{
mvarBlockType = ((AdlibBlockType)blockType);
}
else
{
mvarBlockType = AdlibBlockType.Unknown;
}
mvarPlayerVersion = br.ReadByte(); // Required player version; usually 0x04
mvarTimerSpeed = br.ReadByte(); // Timer speed for the block; usually 0x46
byte numberOfMusicAndSFX = br.ReadByte(); // Number of music and SFX; usually 0x01
byte soundCard = br.ReadByte(); // Soundcard; usually 0x00 - Adlib
if (soundCard == 0)
{
mvarSoundcard = ((AdlibSoundcard)soundCard);
}
else
{
mvarSoundcard = AdlibSoundcard.Unknown;
}
mvarSongTitle = br.ReadFixedLengthString(32); // 32 bytes name of the music
mvarSongArtist = br.ReadFixedLengthString(32); // 32 bytes name of composer
mvarReserved = br.ReadBytes(32); // 32 bytes reserved for future expansion
short ptrTpoin = br.ReadInt16(); // Pointer to "Tpoin" tables
short ptrSeqPointer = br.ReadInt16(); // Pointer to "SeqPointer" tables
short ptrInstrument = br.ReadInt16(); // Pointer to "Instrument" tables
short ptrDataInfo = br.ReadInt16(); // Pointer to "DataInfo" tables
short ptrSpecial = br.ReadInt16(); // Pointer to "Special" tables (SpFX)
short endMark = br.ReadInt16(); // Endmark (0xFFFF)
throw new NotImplementedException();
}
protected override void SaveInternal(ObjectModel objectModel)
{
Writer bw = base.Accessor.Writer;
bw.WriteBytes(new byte[] { 0x4A, 0x43, 0x48, 0x26, 0x02, 0x66 });
switch (mvarBlockType)
{
case AdlibBlockType.MusicData:
{
bw.WriteByte((byte)0);
break;
}
default:
{
bw.WriteByte((byte)0);
break;
}
}
bw.WriteByte(mvarPlayerVersion);
bw.WriteByte(mvarTimerSpeed);
bw.WriteByte(1); // numberOfMusicAndSFX: Number of music and SFX; usually 0x01
switch (mvarSoundcard)
{
case AdlibSoundcard.Adlib:
{
bw.WriteByte((byte)0);
break;
}
default:
{
bw.WriteByte((byte)0);
break;
}
}
bw.WriteFixedLengthString(mvarSongTitle, 32); // 32 bytes name of the music
bw.WriteFixedLengthString(mvarSongArtist, 32); // 32 bytes name of composer
bw.WriteFixedLengthBytes(mvarReserved, 32); // 32 bytes reserved for future expansion
short ptrTpoin = 0;
bw.WriteInt16(ptrTpoin); // Pointer to "Tpoin" tables
short ptrSeqPointer = 0; // Pointer to "SeqPointer" tables
bw.WriteInt16(ptrSeqPointer);
short ptrInstrument = 0; // Pointer to "Instrument" tables
bw.WriteInt16(ptrInstrument);
short ptrDataInfo = 0; // Pointer to "DataInfo" tables
bw.WriteInt16(ptrDataInfo);
short ptrSpecial = 0; // Pointer to "Special" tables (SpFX)
bw.WriteInt16(ptrSpecial);
short endMark = 0; // Endmark (0xFFFF)
bw.WriteInt16(endMark);
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,36 @@
//
// ADXBlock.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2010-2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
namespace UniversalEditor.DataFormats.Multimedia.Audio.Waveform.CRI.ADX
{
public class ADXBlock
{
private ushort mvarScale = 0;
/// <summary>
/// The scale is a 16bit unsigned integer (big-endian like the header) which is essentially the amplification of all the samples in that block.
/// </summary>
public ushort Scale { get { return mvarScale; } set { mvarScale = value; } }
}
}

View File

@ -0,0 +1,266 @@
//
// ADXDocument.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2010-2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
using UniversalEditor.IO;
using UniversalEditor.ObjectModels.Multimedia.Audio.Waveform;
namespace UniversalEditor.DataFormats.Multimedia.Audio.Waveform.CRI.ADX
{
public class ADXDataFormat : DataFormat
{
private byte[] mvarSignature = new byte[] { 0x80, 0x00 };
public byte[] Signature { get { return mvarSignature; } set { mvarSignature = value; } }
public ushort CopyrightOffset { get { return (ushort)(mvarAdditionalPadding.Length + 2); } }
private ADXEncodingType mvarEncodingType = ADXEncodingType.StandardADX;
public ADXEncodingType EncodingType { get { return mvarEncodingType; } set { mvarEncodingType = value; } }
private byte mvarBlockSize = 0;
public byte BlockSize { get { return mvarBlockSize; } set { mvarBlockSize = value; } }
private byte mvarSampleBitDepth = 0;
public byte SampleBitDepth { get { return mvarSampleBitDepth; } set { mvarSampleBitDepth = value; } }
private byte mvarChannelCount = 0;
public byte ChannelCount { get { return mvarChannelCount; } set { mvarChannelCount = value; } }
private uint mvarSampleRate = 0;
public uint SampleRate { get { return mvarSampleRate; } set { mvarSampleRate = value; } }
private uint mvarTotalSamples = 0;
public uint TotalSamples { get { return mvarTotalSamples; } set { mvarTotalSamples = value; } }
private ushort mvarHighpassFrequency = 0;
public ushort HighpassFrequency { get { return mvarHighpassFrequency; } set { mvarHighpassFrequency = value; } }
private ADXVersion mvarVersion = ADXVersion.ADXVersion3;
public ADXVersion Version { get { return mvarVersion; } set { mvarVersion = value; } }
private byte mvarFlags = 0;
public byte Flags { get { return mvarFlags; } set { mvarFlags = value; } }
private uint mvarUnknown = 0;
public uint Unknown { get { return mvarUnknown; } set { mvarUnknown = value; } }
private bool mvarLoopEnabled = false; // uint
public bool LoopEnabled { get { return mvarLoopEnabled; } set { mvarLoopEnabled = value; } }
private uint mvarLoopBeginSampleIndex = 0;
public uint LoopBeginSampleIndex { get { return mvarLoopBeginSampleIndex; } set { mvarLoopBeginSampleIndex = value; } }
private uint mvarLoopBeginByteIndex = 0;
public uint LoopBeginByteIndex { get { return mvarLoopBeginByteIndex; } set { mvarLoopBeginByteIndex = value; } }
private uint mvarLoopEndSampleIndex = 0;
public uint LoopEndSampleIndex { get { return mvarLoopEndSampleIndex; } set { mvarLoopEndSampleIndex = value; } }
private uint mvarLoopEndByteIndex = 0;
public uint LoopEndByteIndex { get { return mvarLoopEndByteIndex; } set { mvarLoopEndByteIndex = value; } }
private byte[] mvarAdditionalPadding = new byte[] { };
public byte[] AdditionalPadding { get { return mvarAdditionalPadding; } set { mvarAdditionalPadding = value; } }
public double[] GetPredictionCoefficients()
{
double M_PI = Math.Acos(-1.0);
double a, b, c;
a = Math.Sqrt(2.0) - Math.Cos(2.0 * M_PI * ((double)mvarHighpassFrequency / mvarSampleRate));
b = Math.Sqrt(2.0) - 1.0;
c = (a - Math.Sqrt((a + b) * (a - b))) / b; //(a+b)*(a-b) = a*a-b*b, however the simpler formula loses accuracy in floating point
double[] coefficient = new double[] { c * 2.0, -(c * c) };
return coefficient;
}
public uint decode_adx_standard(ref short[] buffer, uint samples_needed, bool looping_enabled)
{
int[] past_samples = new int[mvarChannelCount * 2]; // Previously decoded samples from each channel, zeroed at start (size = 2*channel_count)
uint sample_index = 0; // sample_index is the index of sample set that needs to be decoded next
// buffer is where the decoded samples will be put
// samples_needed states how many sample 'sets' (one sample from every channel) need to be decoded to fill the buffer
// looping_enabled is a boolean flag to control use of the built-in loop
// Returns the number of sample 'sets' in the buffer that could not be filled (EOS)
uint samples_per_block = (uint)((mvarBlockSize - 2) * 8 / mvarSampleBitDepth);
short[] scale = new short[mvarChannelCount];
if (looping_enabled && mvarLoopEnabled) looping_enabled = false;
// Loop until the requested number of samples are decoded, or the end of file is reached
while (samples_needed > 0 && sample_index < mvarTotalSamples)
{
// Calculate the number of samples that are left to be decoded in the current block
uint sample_offset = sample_index % samples_per_block;
uint samples_can_get = samples_per_block - sample_offset;
// Clamp the samples we can get during this run if they won't fit in the buffer
if (samples_can_get > samples_needed)
{
samples_can_get = samples_needed;
}
// Clamp the number of samples to be acquired if the stream isn't long enough or the loop trigger is nearby
if (looping_enabled && sample_index + samples_can_get > mvarLoopEndSampleIndex)
{
samples_can_get = mvarLoopEndSampleIndex - sample_index;
}
else if (sample_index + samples_can_get > mvarTotalSamples)
{
samples_can_get = mvarTotalSamples - sample_index;
}
// Calculate the bit address of the start of the frame that sample_index resides in and record that location
ulong started_at = (ulong)(CopyrightOffset + 4 + sample_index / samples_per_block * mvarBlockSize * mvarChannelCount) * 8;
// Read the scale values from the start of each block in this frame
for (uint i = 0; i < mvarChannelCount; ++i)
{
// assuming system.io.binaryreader br
// br.Seek( started_at + mvarBlockSize * i * 8 );
// scale[i] = br.ReadInt16();
}
// Pre-calculate the stop value for sample_offset
uint sample_endoffset = sample_offset + samples_can_get;
// Save the bitstream address of the first sample immediately after the scale in the first block of the frame
started_at += 16;
double[] coefficient = GetPredictionCoefficients();
int sample_error = 0;
int ptrBuffer = 0;
while (sample_offset < sample_endoffset)
{
for (uint i = 0; i < mvarChannelCount; ++i)
{
// Predict the next sample
double sample_prediction = coefficient[0] * past_samples[i * 2 + 0] + coefficient[1] * past_samples[i * 2 + 1];
// Seek to the sample offset, read and sign extend it to a 32bit integer
// Implementing sign extension is left as an exercise for the reader
// The sign extension will also need to include a endian adjustment if there are more than 8 bits
// br.Seek( started_at + mvarSampleBitDepth * sample_offset + mvarBlockSize * 8 * i );
// int sample_error = br.Read( mvarSampleBitDepth );
// sample_error = sign_extend( sample_error, mvarSampleBitDepth);
// Scale the error correction value
sample_error *= scale[i];
// Calculate the sample by combining the prediction with the error correction
int sample = sample_error + (int)sample_prediction;
// Update the past samples with the newer sample
past_samples[i * 2 + 1] = past_samples[i * 2 + 0];
past_samples[i * 2 + 0] = sample;
// Clamp the decoded sample to the valid range for a 16bit integer
if (sample > 32767)
{
sample = 32767;
}
else if (sample < -32768)
{
sample = -32768;
}
// Save the sample to the buffer then advance one place
buffer[ptrBuffer] = (short)sample; ptrBuffer++;
}
++sample_offset; // We've decoded one sample from every block, advance block offset by 1
++sample_index; // This also means we're one sample further into the stream
--samples_needed; // And so there is one less set of samples that need to be decoded
}
// Check if we hit the loop end marker, if we did we need to jump to the loop start
if (looping_enabled && sample_index == mvarLoopEndSampleIndex)
{
sample_index = mvarLoopBeginSampleIndex;
}
}
return samples_needed;
}
private static DataFormatReference _dfr = null;
protected override DataFormatReference MakeReferenceInternal()
{
if (_dfr == null)
{
_dfr = base.MakeReferenceInternal();
_dfr.Capabilities.Add(typeof(WaveformAudioObjectModel), DataFormatCapabilities.All);
}
return _dfr;
}
protected override void LoadInternal(ref ObjectModel objectModel)
{
WaveformAudioObjectModel wave = (objectModel as WaveformAudioObjectModel);
if (wave == null)
throw new ObjectModelNotSupportedException();
Reader reader = base.Accessor.Reader;
throw new NotImplementedException();
}
protected override void SaveInternal(ObjectModel objectModel)
{
WaveformAudioObjectModel wave = (objectModel as WaveformAudioObjectModel);
if (wave == null)
throw new ObjectModelNotSupportedException();
Writer bw = base.Accessor.Writer;
bw.WriteBytes(mvarSignature);
bw.WriteUInt16(CopyrightOffset);
bw.WriteByte((byte)mvarEncodingType);
bw.WriteByte(mvarBlockSize);
bw.WriteByte(mvarSampleBitDepth);
bw.WriteByte(mvarChannelCount);
bw.WriteUInt32(mvarSampleRate);
bw.WriteUInt32(mvarTotalSamples);
bw.WriteUInt16(mvarHighpassFrequency);
bw.WriteByte((byte)mvarVersion);
bw.WriteByte(mvarFlags);
bw.WriteUInt32(mvarUnknown);
if (mvarVersion == ADXVersion.ADXVersion3 || mvarVersion == ADXVersion.ADXVersion3DifferentDecoder)
{
// Write the v3 fields here
bw.WriteBoolean(mvarLoopEnabled);
bw.WriteUInt32(mvarLoopBeginSampleIndex);
bw.WriteUInt32(mvarLoopBeginByteIndex);
bw.WriteUInt32(mvarLoopEndSampleIndex);
bw.WriteUInt32(mvarLoopEndByteIndex);
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
}
else if (mvarVersion == ADXVersion.ADXVersion4 || mvarVersion == ADXVersion.ADXVersion4WithoutLooping)
{
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBoolean(mvarLoopEnabled);
bw.WriteUInt32(mvarLoopBeginSampleIndex);
bw.WriteUInt32(mvarLoopBeginByteIndex);
bw.WriteUInt32(mvarLoopEndSampleIndex);
bw.WriteUInt32(mvarLoopEndByteIndex);
}
bw.WriteBytes(mvarAdditionalPadding);
bw.WriteFixedLengthString("(c)CRI");
// Audio data starts here!
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,254 @@
//
// ADXDocument.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2010-2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
using UniversalEditor.IO;
namespace UniversalEditor.DataFormats.Multimedia.Audio.Waveform.CRI.ADX
{
public class ADXDataFormat : DataFormat
{
private byte[] mvarSignature = new byte[] { 0x80, 0x00 };
public byte[] Signature { get { return mvarSignature; } set { mvarSignature = value; } }
public ushort CopyrightOffset { get { return (ushort)(mvarAdditionalPadding.Length + 2); } }
private ADXEncodingType mvarEncodingType = ADXEncodingType.StandardADX;
public ADXEncodingType EncodingType { get { return mvarEncodingType; } set { mvarEncodingType = value; } }
private byte mvarBlockSize = 0;
public byte BlockSize { get { return mvarBlockSize; } set { mvarBlockSize = value; } }
private byte mvarSampleBitDepth = 0;
public byte SampleBitDepth { get { return mvarSampleBitDepth; } set { mvarSampleBitDepth = value; } }
private byte mvarChannelCount = 0;
public byte ChannelCount { get { return mvarChannelCount; } set { mvarChannelCount = value; } }
private uint mvarSampleRate = 0;
public uint SampleRate { get { return mvarSampleRate; } set { mvarSampleRate = value; } }
private uint mvarTotalSamples = 0;
public uint TotalSamples { get { return mvarTotalSamples; } set { mvarTotalSamples = value; } }
private ushort mvarHighpassFrequency = 0;
public ushort HighpassFrequency { get { return mvarHighpassFrequency; } set { mvarHighpassFrequency = value; } }
private ADXVersion mvarVersion = ADXVersion.ADXVersion3;
public ADXVersion Version { get { return mvarVersion; } set { mvarVersion = value; } }
private byte mvarFlags = 0;
public byte Flags { get { return mvarFlags; } set { mvarFlags = value; } }
private uint mvarUnknown = 0;
public uint Unknown { get { return mvarUnknown; } set { mvarUnknown = value; } }
private bool mvarLoopEnabled = false; // uint
public bool LoopEnabled { get { return mvarLoopEnabled; } set { mvarLoopEnabled = value; } }
private uint mvarLoopBeginSampleIndex = 0;
public uint LoopBeginSampleIndex { get { return mvarLoopBeginSampleIndex; } set { mvarLoopBeginSampleIndex = value; } }
private uint mvarLoopBeginByteIndex = 0;
public uint LoopBeginByteIndex { get { return mvarLoopBeginByteIndex; } set { mvarLoopBeginByteIndex = value; } }
private uint mvarLoopEndSampleIndex = 0;
public uint LoopEndSampleIndex { get { return mvarLoopEndSampleIndex; } set { mvarLoopEndSampleIndex = value; } }
private uint mvarLoopEndByteIndex = 0;
public uint LoopEndByteIndex { get { return mvarLoopEndByteIndex; } set { mvarLoopEndByteIndex = value; } }
private byte[] mvarAdditionalPadding = new byte[] { };
public byte[] AdditionalPadding { get { return mvarAdditionalPadding; } set { mvarAdditionalPadding = value; } }
public double[] GetPredictionCoefficients()
{
double M_PI = Math.Acos(-1.0);
double a, b, c;
a = Math.Sqrt(2.0) - Math.Cos(2.0 * M_PI * ((double)mvarHighpassFrequency / mvarSampleRate));
b = Math.Sqrt(2.0) - 1.0;
c = (a - Math.Sqrt((a + b) * (a - b))) / b; //(a+b)*(a-b) = a*a-b*b, however the simpler formula loses accuracy in floating point
double[] coefficient = new double[] { c * 2.0, -(c * c) };
return coefficient;
}
public uint decode_adx_standard(ref short[] buffer, uint samples_needed, bool looping_enabled)
{
int[] past_samples = new int[mvarChannelCount * 2]; // Previously decoded samples from each channel, zeroed at start (size = 2*channel_count)
uint sample_index = 0; // sample_index is the index of sample set that needs to be decoded next
// buffer is where the decoded samples will be put
// samples_needed states how many sample 'sets' (one sample from every channel) need to be decoded to fill the buffer
// looping_enabled is a boolean flag to control use of the built-in loop
// Returns the number of sample 'sets' in the buffer that could not be filled (EOS)
uint samples_per_block = (uint)((mvarBlockSize - 2) * 8 / mvarSampleBitDepth);
short[] scale = new short[mvarChannelCount];
if (looping_enabled && mvarLoopEnabled) looping_enabled = false;
// Loop until the requested number of samples are decoded, or the end of file is reached
while (samples_needed > 0 && sample_index < mvarTotalSamples)
{
// Calculate the number of samples that are left to be decoded in the current block
uint sample_offset = sample_index % samples_per_block;
uint samples_can_get = samples_per_block - sample_offset;
// Clamp the samples we can get during this run if they won't fit in the buffer
if (samples_can_get > samples_needed)
{
samples_can_get = samples_needed;
}
// Clamp the number of samples to be acquired if the stream isn't long enough or the loop trigger is nearby
if (looping_enabled && sample_index + samples_can_get > mvarLoopEndSampleIndex)
{
samples_can_get = mvarLoopEndSampleIndex - sample_index;
}
else if (sample_index + samples_can_get > mvarTotalSamples)
{
samples_can_get = mvarTotalSamples - sample_index;
}
// Calculate the bit address of the start of the frame that sample_index resides in and record that location
ulong started_at = (ulong)(CopyrightOffset + 4 + sample_index / samples_per_block * mvarBlockSize * mvarChannelCount) * 8;
// Read the scale values from the start of each block in this frame
for (uint i = 0; i < mvarChannelCount; ++i)
{
// assuming system.io.binaryreader br
// br.Seek( started_at + mvarBlockSize * i * 8 );
// scale[i] = br.ReadInt16();
}
// Pre-calculate the stop value for sample_offset
uint sample_endoffset = sample_offset + samples_can_get;
// Save the bitstream address of the first sample immediately after the scale in the first block of the frame
started_at += 16;
double[] coefficient = GetPredictionCoefficients();
int sample_error = 0;
int ptrBuffer = 0;
while (sample_offset < sample_endoffset)
{
for (uint i = 0; i < mvarChannelCount; ++i)
{
// Predict the next sample
double sample_prediction = coefficient[0] * past_samples[i * 2 + 0] + coefficient[1] * past_samples[i * 2 + 1];
// Seek to the sample offset, read and sign extend it to a 32bit integer
// Implementing sign extension is left as an exercise for the reader
// The sign extension will also need to include a endian adjustment if there are more than 8 bits
// br.Seek( started_at + mvarSampleBitDepth * sample_offset + mvarBlockSize * 8 * i );
// int sample_error = br.Read( mvarSampleBitDepth );
// sample_error = sign_extend( sample_error, mvarSampleBitDepth);
// Scale the error correction value
sample_error *= scale[i];
// Calculate the sample by combining the prediction with the error correction
int sample = sample_error + (int)sample_prediction;
// Update the past samples with the newer sample
past_samples[i * 2 + 1] = past_samples[i * 2 + 0];
past_samples[i * 2 + 0] = sample;
// Clamp the decoded sample to the valid range for a 16bit integer
if (sample > 32767)
{
sample = 32767;
}
else if (sample < -32768)
{
sample = -32768;
}
// Save the sample to the buffer then advance one place
buffer[ptrBuffer] = (short)sample; ptrBuffer++;
}
++sample_offset; // We've decoded one sample from every block, advance block offset by 1
++sample_index; // This also means we're one sample further into the stream
--samples_needed; // And so there is one less set of samples that need to be decoded
}
// Check if we hit the loop end marker, if we did we need to jump to the loop start
if (looping_enabled && sample_index == mvarLoopEndSampleIndex)
{
sample_index = mvarLoopBeginSampleIndex;
}
}
return samples_needed;
}
protected override void LoadInternal(ref ObjectModel objectModel)
{
WaveformAudioObjectModel wave = (objectModel as WaveformAudioObjectModel);
if (wave == null)
throw new ObjectModelNotSupportedException();
Reader reader = base.Accessor.Reader;
throw new NotImplementedException();
}
protected override void SaveInternal(ObjectModel objectModel)
{
WaveformAudioObjectModel wave = (objectModel as WaveformAudioObjectModel);
if (wave == null)
throw new ObjectModelNotSupportedException();
Writer bw = base.Accessor.Writer;
bw.WriteBytes(mvarSignature);
bw.WriteUInt16(CopyrightOffset);
bw.WriteByte((byte)mvarEncodingType);
bw.WriteByte(mvarBlockSize);
bw.WriteByte(mvarSampleBitDepth);
bw.WriteByte(mvarChannelCount);
bw.WriteUInt32(mvarSampleRate);
bw.WriteUInt32(mvarTotalSamples);
bw.WriteUInt16(mvarHighpassFrequency);
bw.WriteByte((byte)mvarVersion);
bw.WriteByte(mvarFlags);
bw.WriteUInt32(mvarUnknown);
if (mvarVersion == ADXVersion.ADXVersion3 || mvarVersion == ADXVersion.ADXVersion3DifferentDecoder)
{
// Write the v3 fields here
bw.WriteBoolean(mvarLoopEnabled);
bw.WriteUInt32(mvarLoopBeginSampleIndex);
bw.WriteUInt32(mvarLoopBeginByteIndex);
bw.WriteUInt32(mvarLoopEndSampleIndex);
bw.WriteUInt32(mvarLoopEndByteIndex);
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
}
else if (mvarVersion == ADXVersion.ADXVersion4 || mvarVersion == ADXVersion.ADXVersion4WithoutLooping)
{
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBytes(new byte[] { 0, 0, 0, 0 });
bw.WriteBoolean(mvarLoopEnabled);
bw.WriteUInt32(mvarLoopBeginSampleIndex);
bw.WriteUInt32(mvarLoopBeginByteIndex);
bw.WriteUInt32(mvarLoopEndSampleIndex);
bw.WriteUInt32(mvarLoopEndByteIndex);
}
bw.WriteBytes(mvarAdditionalPadding);
bw.WriteFixedLengthString("(c)CRI");
// Audio data starts here!
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,35 @@
//
// ADXEncodingType.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2010-2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
namespace UniversalEditor.DataFormats.Multimedia.Audio.Waveform.CRI.ADX
{
public enum ADXEncodingType : byte
{
StandardADX = 0x03,
ADXWithExponentialScale = 0x04,
AHX10 = 0x10,
AHX11 = 0x11
}
}

View File

@ -0,0 +1,35 @@
//
// ADXVersion.cs
//
// Author:
// Mike Becker <alcexhim@gmail.com>
//
// Copyright (c) 2010-2019 Mike Becker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
namespace UniversalEditor.DataFormats.Multimedia.Audio.Waveform.CRI.ADX
{
public enum ADXVersion : byte
{
ADXVersion3DifferentDecoder = 0x02,
ADXVersion3 = 0x03,
ADXVersion4 = 0x04,
ADXVersion4WithoutLooping = 0x05
}
}

View File

@ -316,6 +316,13 @@
<Compile Include="DataFormats\Multimedia\Picture\Adobe\Photoshop\PSDLayerMaskParameterFlags.cs" />
<Compile Include="DataFormats\Multimedia\Picture\Adobe\Photoshop\PSDLayerCompressionMode.cs" />
<Compile Include="DataFormats\Multimedia\Picture\Adobe\Photoshop\Internal\PSDLayer.cs" />
<Compile Include="DataFormats\Multimedia\Audio\Waveform\CRI\ADX\ADXBlock.cs" />
<Compile Include="DataFormats\Multimedia\Audio\Waveform\CRI\ADX\ADXDataFormat.cs" />
<Compile Include="DataFormats\Multimedia\Audio\Waveform\CRI\ADX\ADXEncodingType.cs" />
<Compile Include="DataFormats\Multimedia\Audio\Waveform\CRI\ADX\ADXVersion.cs" />
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\EdLib\D00DataFormat.cs" />
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\EdLib\AdlibSoundcard.cs" />
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\EdLib\AdlibBlockType.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Libraries\UniversalEditor.Compression\UniversalEditor.Compression.csproj">
@ -363,6 +370,9 @@
<Folder Include="DataFormats\Multimedia\Picture\Adobe\" />
<Folder Include="DataFormats\Multimedia\Picture\Adobe\Photoshop\" />
<Folder Include="DataFormats\Multimedia\Picture\Adobe\Photoshop\Internal\" />
<Folder Include="DataFormats\Multimedia\Audio\Waveform\CRI\" />
<Folder Include="DataFormats\Multimedia\Audio\Waveform\CRI\ADX\" />
<Folder Include="DataFormats\Multimedia\Audio\Synthesized\EdLib\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>