mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
C# code for APE tags
This commit is contained in:
@@ -332,6 +332,10 @@
|
|||||||
RelativePath="System.XML.dll"
|
RelativePath="System.XML.dll"
|
||||||
AssemblyName="System.Xml, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
|
AssemblyName="System.Xml, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
|
||||||
/>
|
/>
|
||||||
|
<ProjectReference
|
||||||
|
ReferencedProjectIdentifier="{CA200BCB-DFC6-4153-9BD4-785BC768B26B}"
|
||||||
|
RelativePathToProject="..\APETagDotNet\APETagDotNet.csproj"
|
||||||
|
/>
|
||||||
</References>
|
</References>
|
||||||
<Files>
|
<Files>
|
||||||
<Filter
|
<Filter
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using namespace System::Text;
|
|||||||
using namespace System::Collections::Generic;
|
using namespace System::Collections::Generic;
|
||||||
using namespace System::Collections::Specialized;
|
using namespace System::Collections::Specialized;
|
||||||
using namespace System::Runtime::InteropServices;
|
using namespace System::Runtime::InteropServices;
|
||||||
|
using namespace APETagsDotNet;
|
||||||
|
|
||||||
#ifndef _WAVEFORMATEX_
|
#ifndef _WAVEFORMATEX_
|
||||||
#define _WAVEFORMATEX_
|
#define _WAVEFORMATEX_
|
||||||
@@ -34,7 +35,6 @@ typedef struct tWAVEFORMATEX
|
|||||||
|
|
||||||
#include "All.h"
|
#include "All.h"
|
||||||
#include "MACLib.h"
|
#include "MACLib.h"
|
||||||
#include "APETag.h"
|
|
||||||
|
|
||||||
namespace APEDotNet {
|
namespace APEDotNet {
|
||||||
|
|
||||||
@@ -46,18 +46,18 @@ namespace APEDotNet {
|
|||||||
pAPEDecompress = NULL;
|
pAPEDecompress = NULL;
|
||||||
_sampleOffset = 0;
|
_sampleOffset = 0;
|
||||||
_samplesWaiting = false;
|
_samplesWaiting = false;
|
||||||
_path = NULL;
|
_path = path;
|
||||||
pBuffer = NULL;
|
pBuffer = NULL;
|
||||||
|
|
||||||
int nRetVal = 0;
|
int nRetVal = 0;
|
||||||
|
|
||||||
pathChars = Marshal::StringToHGlobalUni(path);
|
pathChars = Marshal::StringToHGlobalUni(path);
|
||||||
size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1;
|
size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1;
|
||||||
_path = new wchar_t[pathLen];
|
wchar_t * pPath = new wchar_t[pathLen];
|
||||||
memcpy ((void*) _path, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t));
|
memcpy ((void*) pPath, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t));
|
||||||
Marshal::FreeHGlobal(pathChars);
|
Marshal::FreeHGlobal(pathChars);
|
||||||
|
|
||||||
pAPEDecompress = CreateIAPEDecompress (_path, &nRetVal);
|
pAPEDecompress = CreateIAPEDecompress (pPath, &nRetVal);
|
||||||
if (!pAPEDecompress) {
|
if (!pAPEDecompress) {
|
||||||
throw gcnew Exception("Unable to open file.");
|
throw gcnew Exception("Unable to open file.");
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,6 @@ namespace APEDotNet {
|
|||||||
~APEReader ()
|
~APEReader ()
|
||||||
{
|
{
|
||||||
if (pBuffer) delete [] pBuffer;
|
if (pBuffer) delete [] pBuffer;
|
||||||
if (_path) delete [] _path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
property Int32 BitsPerSample {
|
property Int32 BitsPerSample {
|
||||||
@@ -129,7 +128,7 @@ namespace APEDotNet {
|
|||||||
|
|
||||||
property NameValueCollection^ Tags {
|
property NameValueCollection^ Tags {
|
||||||
NameValueCollection^ get () {
|
NameValueCollection^ get () {
|
||||||
if (!_tags) GetTags ();
|
if (!_tags) _tags = (gcnew APETagDotNet (_path, true))->GetStringTags (true);
|
||||||
return _tags;
|
return _tags;
|
||||||
}
|
}
|
||||||
void set (NameValueCollection ^tags) {
|
void set (NameValueCollection ^tags) {
|
||||||
@@ -180,32 +179,7 @@ namespace APEDotNet {
|
|||||||
int nBlockAlign;
|
int nBlockAlign;
|
||||||
bool _samplesWaiting;
|
bool _samplesWaiting;
|
||||||
unsigned char * pBuffer;
|
unsigned char * pBuffer;
|
||||||
const wchar_t * _path;
|
String^ _path;
|
||||||
|
|
||||||
void GetTags (void)
|
|
||||||
{
|
|
||||||
_tags = gcnew NameValueCollection();
|
|
||||||
CAPETag apeTag (_path, TRUE);
|
|
||||||
for (int i = 0; ; i++)
|
|
||||||
{
|
|
||||||
CAPETagField * field = apeTag.GetTagField (i);
|
|
||||||
if (!field)
|
|
||||||
break;
|
|
||||||
if (field->GetIsUTF8Text())
|
|
||||||
{
|
|
||||||
int valueSize = field->GetFieldValueSize();
|
|
||||||
while (valueSize && field->GetFieldValue()[valueSize-1]=='\0')
|
|
||||||
valueSize --;
|
|
||||||
const wchar_t * fieldName = field->GetFieldName ();
|
|
||||||
if (!wcsicmp (fieldName, L"YEAR"))
|
|
||||||
fieldName = L"DATE";
|
|
||||||
if (!wcsicmp (fieldName, L"TRACK"))
|
|
||||||
fieldName = L"TRACKNUMBER";
|
|
||||||
_tags->Add (gcnew String (fieldName),
|
|
||||||
gcnew String (field->GetFieldValue(), 0, valueSize, System::Text::Encoding::UTF8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
APE__StreamDecoderWriteStatus WriteCallback(const APE__StreamDecoder *decoder,
|
APE__StreamDecoderWriteStatus WriteCallback(const APE__StreamDecoder *decoder,
|
||||||
|
|||||||
608
APETagDotNet/APETagDotNet.cs
Normal file
608
APETagDotNet/APETagDotNet.cs
Normal file
@@ -0,0 +1,608 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace APETagsDotNet
|
||||||
|
{
|
||||||
|
public class LittleEndian
|
||||||
|
{
|
||||||
|
static public Int32 Read32 (byte[] buffer, int offset)
|
||||||
|
{
|
||||||
|
return buffer[offset] + (buffer[offset + 1] << 8) + (buffer[offset + 2] << 16) + (buffer[offset + 3] << 24);
|
||||||
|
}
|
||||||
|
static public void Write32(byte[] buffer, int offset, Int32 value)
|
||||||
|
{
|
||||||
|
buffer[offset++] = (byte) (value & 0xff); value >>= 8;
|
||||||
|
buffer[offset++] = (byte) (value & 0xff); value >>= 8;
|
||||||
|
buffer[offset++] = (byte) (value & 0xff); value >>= 8;
|
||||||
|
buffer[offset++] = (byte) (value & 0xff); value >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class APE_TAG_FOOTER
|
||||||
|
{
|
||||||
|
public APE_TAG_FOOTER(int nFields, int nFieldBytes)
|
||||||
|
{
|
||||||
|
m_cID = new ASCIIEncoding().GetBytes ("APETAGEX");
|
||||||
|
m_cReserved = new byte[8];
|
||||||
|
m_nFields = nFields;
|
||||||
|
m_nFlags = APETagDotNet.APE_TAG_FLAGS_DEFAULT;
|
||||||
|
m_nSize = nFieldBytes + APETagDotNet.APE_TAG_FOOTER_BYTES;
|
||||||
|
m_nVersion = APETagDotNet.CURRENT_APE_TAG_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
public APE_TAG_FOOTER(byte[] buffer)
|
||||||
|
{
|
||||||
|
m_cID = new byte[8];
|
||||||
|
m_cReserved = new byte[8];
|
||||||
|
|
||||||
|
Array.Copy(buffer, m_cID, 8);
|
||||||
|
m_nVersion = LittleEndian.Read32(buffer, 8);
|
||||||
|
m_nSize = LittleEndian.Read32(buffer, 12);
|
||||||
|
m_nFields = LittleEndian.Read32(buffer, 16);
|
||||||
|
m_nFlags = LittleEndian.Read32(buffer, 20);
|
||||||
|
Array.Copy (buffer, 24, m_cReserved, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Save(byte[] spRawTag, int nLocation)
|
||||||
|
{
|
||||||
|
Array.Copy(m_cID, 0, spRawTag, nLocation, 8);
|
||||||
|
LittleEndian.Write32(spRawTag, nLocation + 8, m_nVersion);
|
||||||
|
LittleEndian.Write32(spRawTag, nLocation + 12, m_nSize);
|
||||||
|
LittleEndian.Write32(spRawTag, nLocation + 16, m_nFields);
|
||||||
|
LittleEndian.Write32(spRawTag, nLocation + 20, m_nFlags);
|
||||||
|
Array.Copy(m_cReserved, 0, spRawTag, nLocation + 24, 8);
|
||||||
|
return APETagDotNet.APE_TAG_FOOTER_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TotalTagBytes { get { return m_nSize + (HasHeader ? APETagDotNet.APE_TAG_FOOTER_BYTES : 0); } }
|
||||||
|
public int FieldBytes { get { return m_nSize - APETagDotNet.APE_TAG_FOOTER_BYTES; } }
|
||||||
|
public int FieldsOffset { get { return HasHeader ? APETagDotNet.APE_TAG_FOOTER_BYTES : 0; } }
|
||||||
|
public int NumberFields { get { return m_nFields; } }
|
||||||
|
public bool HasHeader { get { return (m_nFlags & APETagDotNet.APE_TAG_FLAG_CONTAINS_HEADER) != 0; } }
|
||||||
|
public bool IsHeader { get { return (m_nFlags & APETagDotNet.APE_TAG_FLAG_IS_HEADER) != 0; } }
|
||||||
|
public int Version { get { return m_nVersion; } }
|
||||||
|
public bool IsValid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new ASCIIEncoding().GetString (m_cID) == "APETAGEX" &&
|
||||||
|
(m_nVersion <= APETagDotNet.CURRENT_APE_TAG_VERSION) &&
|
||||||
|
(m_nFields <= 65536) &&
|
||||||
|
(FieldBytes <= (1024 * 1024 * 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] m_cID; // should equal 'APETAGEX'
|
||||||
|
private int m_nVersion; // equals CURRENT_APE_TAG_VERSION
|
||||||
|
private int m_nSize; // the complete size of the tag, including this footer (excludes header)
|
||||||
|
private int m_nFields; // the number of fields in the tag
|
||||||
|
private int m_nFlags; // the tag flags
|
||||||
|
private byte[] m_cReserved; // reserved for later use (must be zero)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public class APETagField
|
||||||
|
{
|
||||||
|
// create a tag field (use nFieldBytes = -1 for null-terminated strings)
|
||||||
|
public APETagField (string fieldName, byte[] fieldValue, int fieldFlags) {
|
||||||
|
_fieldName = fieldName;
|
||||||
|
_fieldValue = fieldValue;
|
||||||
|
_fieldFlags = fieldFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
~APETagField() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the size of the entire field in bytes (name, value, and metadata)
|
||||||
|
public int GetFieldSize()
|
||||||
|
{
|
||||||
|
return _fieldName.Length + 1 + _fieldValue.Length + 4 + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the name of the field
|
||||||
|
public string FieldName { get { return _fieldName; } }
|
||||||
|
|
||||||
|
// get the value of the field
|
||||||
|
public byte[] FieldValue { get { return _fieldValue; } }
|
||||||
|
|
||||||
|
// output the entire field to a buffer (GetFieldSize() bytes)
|
||||||
|
public int SaveField(byte[] pBuffer, int pos)
|
||||||
|
{
|
||||||
|
LittleEndian.Write32(pBuffer, pos, _fieldValue.Length);
|
||||||
|
LittleEndian.Write32(pBuffer, pos + 4, _fieldFlags);
|
||||||
|
Array.Copy (new ASCIIEncoding().GetBytes(_fieldName), pBuffer, pos + 8);
|
||||||
|
pBuffer[pos + 8 + _fieldName.Length] = 0;
|
||||||
|
Array.Copy(_fieldValue, pBuffer, pos + 8 + _fieldName.Length + 1);
|
||||||
|
return GetFieldSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks to see if the field is read-only
|
||||||
|
public bool IsReadOnly { get { return (_fieldFlags & APETagDotNet.TAG_FIELD_FLAG_READ_ONLY) != 0; } }
|
||||||
|
public bool IsUTF8Text { get { return (_fieldFlags & APETagDotNet.TAG_FIELD_FLAG_DATA_TYPE_MASK) == APETagDotNet.TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8; } }
|
||||||
|
|
||||||
|
// set helpers (use with EXTREME caution)
|
||||||
|
public int FieldFlags {
|
||||||
|
get { return _fieldFlags; }
|
||||||
|
set { _fieldFlags = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _fieldName;
|
||||||
|
private byte[] _fieldValue;
|
||||||
|
private int _fieldFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
public class APETagDotNet
|
||||||
|
{
|
||||||
|
/*****************************************************************************************
|
||||||
|
The version of the APE tag
|
||||||
|
*****************************************************************************************/
|
||||||
|
public const int CURRENT_APE_TAG_VERSION = 2000;
|
||||||
|
|
||||||
|
public const int ID3_TAG_BYTES = 128;
|
||||||
|
|
||||||
|
/*****************************************************************************************
|
||||||
|
Footer (and header) flags
|
||||||
|
*****************************************************************************************/
|
||||||
|
public const int APE_TAG_FLAG_CONTAINS_HEADER = (1 << 31);
|
||||||
|
public const int APE_TAG_FLAG_CONTAINS_FOOTER = (1 << 30);
|
||||||
|
public const int APE_TAG_FLAG_IS_HEADER = (1 << 29);
|
||||||
|
|
||||||
|
public const int APE_TAG_FLAGS_DEFAULT = (APE_TAG_FLAG_CONTAINS_FOOTER);
|
||||||
|
|
||||||
|
/*****************************************************************************************
|
||||||
|
Tag field flags
|
||||||
|
*****************************************************************************************/
|
||||||
|
public const int TAG_FIELD_FLAG_READ_ONLY = (1 << 0);
|
||||||
|
|
||||||
|
public const int TAG_FIELD_FLAG_DATA_TYPE_MASK = (6);
|
||||||
|
public const int TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8 = (0 << 1);
|
||||||
|
public const int TAG_FIELD_FLAG_DATA_TYPE_BINARY = (1 << 1);
|
||||||
|
public const int TAG_FIELD_FLAG_DATA_TYPE_EXTERNAL_INFO = (2 << 1);
|
||||||
|
public const int TAG_FIELD_FLAG_DATA_TYPE_RESERVED = (3 << 1);
|
||||||
|
|
||||||
|
/*****************************************************************************************
|
||||||
|
The footer at the end of APE tagged files (can also optionally be at the front of the tag)
|
||||||
|
*****************************************************************************************/
|
||||||
|
public const int APE_TAG_FOOTER_BYTES = 32;
|
||||||
|
|
||||||
|
|
||||||
|
// create an APE tags object
|
||||||
|
// bAnalyze determines whether it will analyze immediately or on the first request
|
||||||
|
// be careful with multiple threads / file pointer movement if you don't analyze immediately
|
||||||
|
public APETagDotNet (string filename, bool analyze)
|
||||||
|
{
|
||||||
|
m_spIO = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
m_bAnalyzed = false;
|
||||||
|
m_aryFields = new APETagField[0];
|
||||||
|
m_nTagBytes = 0;
|
||||||
|
m_bIgnoreReadOnly = false;
|
||||||
|
if (analyze) Analyze ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
~APETagDotNet () { ClearFields (); }
|
||||||
|
|
||||||
|
// save the tag to the I/O source (bUseOldID3 forces it to save as an ID3v1.1 tag instead of an APE tag)
|
||||||
|
int Save ()
|
||||||
|
{
|
||||||
|
if (!Remove(false))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (m_aryFields.Length == 0) { return 0; }
|
||||||
|
|
||||||
|
int z = 0;
|
||||||
|
|
||||||
|
// calculate the size of the whole tag
|
||||||
|
int nFieldBytes = 0;
|
||||||
|
for (z = 0; z < m_aryFields.Length; z++)
|
||||||
|
nFieldBytes += m_aryFields[z].GetFieldSize();
|
||||||
|
|
||||||
|
// sort the fields
|
||||||
|
SortFields();
|
||||||
|
|
||||||
|
// build the footer
|
||||||
|
APE_TAG_FOOTER APETagFooter = new APE_TAG_FOOTER(m_aryFields.Length, nFieldBytes);
|
||||||
|
|
||||||
|
// make a buffer for the tag
|
||||||
|
int nTotalTagBytes = APETagFooter.TotalTagBytes;
|
||||||
|
byte[] spRawTag = new byte[APETagFooter.TotalTagBytes];
|
||||||
|
|
||||||
|
// save the fields
|
||||||
|
int nLocation = 0;
|
||||||
|
for (z = 0; z < m_aryFields.Length; z++)
|
||||||
|
nLocation += m_aryFields[z].SaveField (spRawTag, nLocation);
|
||||||
|
|
||||||
|
// add the footer to the buffer
|
||||||
|
nLocation += APETagFooter.Save(spRawTag, nLocation);
|
||||||
|
|
||||||
|
// dump the tag to the I/O source
|
||||||
|
WriteBufferToEndOfIO (spRawTag);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes any tags from the file (bUpdate determines whether is should re-analyze after removing the tag)
|
||||||
|
bool Remove(bool bUpdate)
|
||||||
|
{
|
||||||
|
// variables
|
||||||
|
int nBytesRead = 0;
|
||||||
|
long nOriginalPosition = m_spIO.Position;
|
||||||
|
|
||||||
|
bool bID3Removed = true;
|
||||||
|
bool bAPETagRemoved = true;
|
||||||
|
|
||||||
|
bool bFailedToRemove = false;
|
||||||
|
|
||||||
|
while (bID3Removed || bAPETagRemoved)
|
||||||
|
{
|
||||||
|
bID3Removed = false;
|
||||||
|
bAPETagRemoved = false;
|
||||||
|
|
||||||
|
// ID3 tag
|
||||||
|
if (m_spIO.Length > ID3_TAG_BYTES)
|
||||||
|
{
|
||||||
|
byte[] cTagHeader = new byte[3];
|
||||||
|
m_spIO.Seek (-ID3_TAG_BYTES, SeekOrigin.End);
|
||||||
|
nBytesRead = m_spIO.Read (cTagHeader, 0, 3);
|
||||||
|
if (nBytesRead == 3)
|
||||||
|
{
|
||||||
|
if (cTagHeader[0]=='T' && cTagHeader[1]=='A' && cTagHeader[2]=='G')
|
||||||
|
{
|
||||||
|
try { m_spIO.SetLength(m_spIO.Length - ID3_TAG_BYTES); }
|
||||||
|
catch { bFailedToRemove = true; }
|
||||||
|
if (!bFailedToRemove)
|
||||||
|
bID3Removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// APE Tag
|
||||||
|
if (m_spIO.Length > APE_TAG_FOOTER_BYTES && bFailedToRemove == false)
|
||||||
|
{
|
||||||
|
APE_TAG_FOOTER APETagFooter;
|
||||||
|
m_spIO.Seek(-APE_TAG_FOOTER_BYTES, SeekOrigin.End);
|
||||||
|
byte [] buf = new byte[APE_TAG_FOOTER_BYTES];
|
||||||
|
nBytesRead = m_spIO.Read(buf, 0, APE_TAG_FOOTER_BYTES);
|
||||||
|
if (nBytesRead == APE_TAG_FOOTER_BYTES)
|
||||||
|
{
|
||||||
|
APETagFooter = new APE_TAG_FOOTER(buf);
|
||||||
|
if (APETagFooter.IsValid)
|
||||||
|
{
|
||||||
|
try { m_spIO.SetLength(m_spIO.Length - APETagFooter.TotalTagBytes); }
|
||||||
|
catch { bFailedToRemove = true; }
|
||||||
|
if (!bFailedToRemove)
|
||||||
|
bAPETagRemoved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
m_spIO.Seek(nOriginalPosition, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
if (bUpdate && bFailedToRemove == false)
|
||||||
|
{
|
||||||
|
m_bAnalyzed = false;
|
||||||
|
Analyze();
|
||||||
|
}
|
||||||
|
return !bFailedToRemove;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets the value of a field (use nFieldBytes = -1 for null terminated strings)
|
||||||
|
// note: using NULL or "" for a string type will remove the field
|
||||||
|
void SetFieldString(string fieldName, string fieldValue)
|
||||||
|
{
|
||||||
|
// remove if empty
|
||||||
|
if (fieldValue == "")
|
||||||
|
{
|
||||||
|
RemoveField(fieldName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UTF8Encoding enc = new UTF8Encoding();
|
||||||
|
// UTF-8 encode the value and call the binary SetField(...)
|
||||||
|
SetFieldBinary (fieldName, enc.GetBytes (fieldValue), TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8);
|
||||||
|
}
|
||||||
|
//int SetFieldString(string pFieldName, const char * pFieldValue, bool bAlreadyUTF8Encoded);
|
||||||
|
void SetFieldBinary(string fieldName, byte[] fieldValue, int fieldFlags)
|
||||||
|
{
|
||||||
|
Analyze();
|
||||||
|
|
||||||
|
// check to see if we're trying to remove the field (by setting it to NULL or an empty string)
|
||||||
|
bool bRemoving = (fieldValue.Length == 0);
|
||||||
|
|
||||||
|
// get the index
|
||||||
|
int nFieldIndex = GetTagFieldIndex (fieldName);
|
||||||
|
if (nFieldIndex != -1)
|
||||||
|
{
|
||||||
|
// existing field
|
||||||
|
|
||||||
|
// fail if we're read-only (and not ignoring the read-only flag)
|
||||||
|
if (!m_bIgnoreReadOnly && (m_aryFields[nFieldIndex].IsReadOnly))
|
||||||
|
throw new Exception("read only");
|
||||||
|
|
||||||
|
// erase the existing field
|
||||||
|
//SAFE_DELETE(m_aryFields[nFieldIndex])
|
||||||
|
|
||||||
|
if (bRemoving)
|
||||||
|
{
|
||||||
|
RemoveField(nFieldIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (bRemoving)
|
||||||
|
return;
|
||||||
|
nFieldIndex = m_aryFields.Length;
|
||||||
|
Array.Resize (ref m_aryFields, nFieldIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the field and add it to the field array
|
||||||
|
m_aryFields[nFieldIndex] = new APETagField (fieldName, fieldValue, fieldFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the value of a field (returns -1 and an empty buffer if the field doesn't exist)
|
||||||
|
byte[] GetFieldBinary(string pFieldName)
|
||||||
|
{
|
||||||
|
Analyze();
|
||||||
|
APETagField pAPETagField = GetTagField (pFieldName);
|
||||||
|
return (pAPETagField == null) ? null : pAPETagField.FieldValue;
|
||||||
|
}
|
||||||
|
public int Count { get { Analyze(); return m_aryFields.Length; } }
|
||||||
|
public string GetFieldString(string pFieldName) { return GetFieldString(GetTagFieldIndex(pFieldName)); }
|
||||||
|
public string GetFieldString(int Index)
|
||||||
|
{
|
||||||
|
Analyze();
|
||||||
|
APETagField pAPETagField = GetTagField (Index);
|
||||||
|
if (pAPETagField == null)
|
||||||
|
return null;
|
||||||
|
if (m_nAPETagVersion < 2000)
|
||||||
|
return new ASCIIEncoding().GetString(pAPETagField.FieldValue);
|
||||||
|
if (!pAPETagField.IsUTF8Text)
|
||||||
|
return null;
|
||||||
|
return new UTF8Encoding().GetString(pAPETagField.FieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NameValueCollection GetStringTags(bool mapToFlac)
|
||||||
|
{
|
||||||
|
Analyze();
|
||||||
|
NameValueCollection tags = new NameValueCollection ();
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
string fieldName = m_aryFields[i].FieldName;
|
||||||
|
string fieldValue = GetFieldString (i);
|
||||||
|
if (fieldName != null && fieldValue != null)
|
||||||
|
{
|
||||||
|
if (mapToFlac)
|
||||||
|
{
|
||||||
|
if (fieldName.ToUpper() == "YEAR")
|
||||||
|
fieldName = "DATE";
|
||||||
|
if (fieldName.ToUpper() == "TRACK")
|
||||||
|
fieldName = "TRACKNUMBER";
|
||||||
|
}
|
||||||
|
tags.Add (fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// remove a specific field
|
||||||
|
void RemoveField(string pFieldName)
|
||||||
|
{
|
||||||
|
RemoveField(GetTagFieldIndex(pFieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveField(int nIndex)
|
||||||
|
{
|
||||||
|
for (int i = nIndex; i < m_aryFields.Length-1; i++)
|
||||||
|
m_aryFields[i] = m_aryFields[i+1];
|
||||||
|
Array.Resize (ref m_aryFields, m_aryFields.Length-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear all the fields
|
||||||
|
void ClearFields() { m_aryFields = new APETagField[0]; }
|
||||||
|
|
||||||
|
// get the total tag bytes in the file from the last analyze
|
||||||
|
// need to call Save() then Analyze() to update any changes
|
||||||
|
int GetTagBytes() {
|
||||||
|
Analyze();
|
||||||
|
return m_nTagBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fills in an ID3_TAG using the current fields (useful for quickly converting the tag)
|
||||||
|
//int CreateID3Tag(ID3_TAG * pID3Tag);
|
||||||
|
|
||||||
|
// see whether the file has an ID3 or APE tag
|
||||||
|
//bool GetHasID3Tag() { if (!m_bAnalyzed) { Analyze(); } return m_bHasID3Tag; }
|
||||||
|
bool GetHasAPETag() { Analyze(); return m_bHasAPETag; }
|
||||||
|
int GetAPETagVersion() { return GetHasAPETag() ? m_nAPETagVersion : -1; }
|
||||||
|
|
||||||
|
// gets a desired tag field (returns NULL if not found)
|
||||||
|
// again, be careful, because this a pointer to the actual field in this class
|
||||||
|
APETagField GetTagField(string pFieldName)
|
||||||
|
{
|
||||||
|
int nIndex = GetTagFieldIndex (pFieldName);
|
||||||
|
return (nIndex != -1) ? m_aryFields[nIndex] : null;
|
||||||
|
}
|
||||||
|
public APETagField GetTagField(int nIndex)
|
||||||
|
{
|
||||||
|
Analyze();
|
||||||
|
if (nIndex >= 0 && nIndex < m_aryFields.Length)
|
||||||
|
return m_aryFields[nIndex];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// options
|
||||||
|
void SetIgnoreReadOnly(bool bIgnoreReadOnly) { m_bIgnoreReadOnly = bIgnoreReadOnly; }
|
||||||
|
|
||||||
|
// private functions
|
||||||
|
private int Analyze()
|
||||||
|
{
|
||||||
|
if (m_bAnalyzed)
|
||||||
|
return 0;
|
||||||
|
// clean-up
|
||||||
|
// ID3_TAG ID3Tag;
|
||||||
|
ClearFields();
|
||||||
|
m_nTagBytes = 0;
|
||||||
|
|
||||||
|
m_bAnalyzed = true;
|
||||||
|
|
||||||
|
// store the original location
|
||||||
|
long nOriginalPosition = m_spIO.Position;
|
||||||
|
|
||||||
|
// check for a tag
|
||||||
|
int nBytesRead;
|
||||||
|
//m_bHasID3Tag = false;
|
||||||
|
m_bHasAPETag = false;
|
||||||
|
m_nAPETagVersion = -1;
|
||||||
|
|
||||||
|
//m_spIO->Seek(-ID3_TAG_BYTES, FILE_END);
|
||||||
|
//nRetVal = m_spIO->Read((unsigned char *) &ID3Tag, sizeof(ID3_TAG), &nBytesRead);
|
||||||
|
//
|
||||||
|
//if ((nBytesRead == sizeof(ID3_TAG)) && (nRetVal == 0))
|
||||||
|
//{
|
||||||
|
// if (ID3Tag.Header[0] == 'T' && ID3Tag.Header[1] == 'A' && ID3Tag.Header[2] == 'G')
|
||||||
|
// {
|
||||||
|
// m_bHasID3Tag = true;
|
||||||
|
// m_nTagBytes += ID3_TAG_BYTES;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// set the fields
|
||||||
|
//if (m_bHasID3Tag)
|
||||||
|
//{
|
||||||
|
// SetFieldID3String(APE_TAG_FIELD_ARTIST, ID3Tag.Artist, 30);
|
||||||
|
// SetFieldID3String(APE_TAG_FIELD_ALBUM, ID3Tag.Album, 30);
|
||||||
|
// SetFieldID3String(APE_TAG_FIELD_TITLE, ID3Tag.Title, 30);
|
||||||
|
// SetFieldID3String(APE_TAG_FIELD_COMMENT, ID3Tag.Comment, 28);
|
||||||
|
// SetFieldID3String(APE_TAG_FIELD_YEAR, ID3Tag.Year, 4);
|
||||||
|
//
|
||||||
|
// char cTemp[16]; sprintf(cTemp, "%d", ID3Tag.Track);
|
||||||
|
// SetFieldString(APE_TAG_FIELD_TRACK, cTemp, false);
|
||||||
|
|
||||||
|
// if ((ID3Tag.Genre == GENRE_UNDEFINED) || (ID3Tag.Genre >= GENRE_COUNT))
|
||||||
|
// SetFieldString(APE_TAG_FIELD_GENRE, APE_TAG_GENRE_UNDEFINED);
|
||||||
|
// else
|
||||||
|
// SetFieldString(APE_TAG_FIELD_GENRE, g_ID3Genre[ID3Tag.Genre]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
// try loading the APE tag
|
||||||
|
//if (m_bHasID3Tag == false)
|
||||||
|
{
|
||||||
|
byte[] pBuffer = new byte[APE_TAG_FOOTER_BYTES];
|
||||||
|
m_spIO.Seek (-APE_TAG_FOOTER_BYTES, SeekOrigin.End);
|
||||||
|
nBytesRead = m_spIO.Read (pBuffer, 0, APE_TAG_FOOTER_BYTES);
|
||||||
|
if (nBytesRead == APE_TAG_FOOTER_BYTES)
|
||||||
|
{
|
||||||
|
APE_TAG_FOOTER APETagFooter = new APE_TAG_FOOTER(pBuffer);
|
||||||
|
if (APETagFooter.IsValid && !APETagFooter.IsHeader)
|
||||||
|
{
|
||||||
|
m_bHasAPETag = true;
|
||||||
|
m_nAPETagVersion = APETagFooter.Version;
|
||||||
|
|
||||||
|
int nRawFieldBytes = APETagFooter.FieldBytes;
|
||||||
|
m_nTagBytes += APETagFooter.TotalTagBytes;
|
||||||
|
|
||||||
|
byte[] spRawTag = new byte[nRawFieldBytes];
|
||||||
|
m_spIO.Seek(-(APETagFooter.TotalTagBytes - APETagFooter.FieldsOffset), SeekOrigin.End);
|
||||||
|
nBytesRead = m_spIO.Read(spRawTag, 0, nRawFieldBytes);
|
||||||
|
|
||||||
|
if (nRawFieldBytes == nBytesRead)
|
||||||
|
{
|
||||||
|
// parse out the raw fields
|
||||||
|
int nLocation = 0;
|
||||||
|
for (int z = 0; z < APETagFooter.NumberFields; z++)
|
||||||
|
{
|
||||||
|
if (!LoadField(spRawTag, ref nLocation))
|
||||||
|
{
|
||||||
|
// if LoadField(...) fails, it means that the tag is corrupt (accidently or intentionally)
|
||||||
|
// we'll just bail out -- leaving the fields we've already set
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// restore the file pointer
|
||||||
|
m_spIO.Seek(nOriginalPosition, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
private int GetTagFieldIndex(string pFieldName)
|
||||||
|
{
|
||||||
|
Analyze();
|
||||||
|
for (int z = 0; z < m_aryFields.Length; z++)
|
||||||
|
if (m_aryFields[z].FieldName.ToUpper() == pFieldName.ToUpper())
|
||||||
|
return z;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
private void WriteBufferToEndOfIO (byte[] pBuffer)
|
||||||
|
{
|
||||||
|
Int64 nOriginalPosition = m_spIO.Position;
|
||||||
|
m_spIO.Seek(0, SeekOrigin.End);
|
||||||
|
m_spIO.Write(pBuffer, 0, pBuffer.Length);
|
||||||
|
m_spIO.Seek(nOriginalPosition, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
private bool LoadField(byte[] pBuffer, ref int nLocation)
|
||||||
|
{
|
||||||
|
// size and flags
|
||||||
|
int nFieldValueSize = LittleEndian.Read32(pBuffer, nLocation);
|
||||||
|
int nFieldFlags = LittleEndian.Read32(pBuffer, nLocation+4);
|
||||||
|
nLocation += 8;
|
||||||
|
|
||||||
|
// safety check (so we can't get buffer overflow attacked)
|
||||||
|
int nMaximumRead = pBuffer.Length - nLocation - nFieldValueSize;
|
||||||
|
for (int z = 0; z < nMaximumRead; z++)
|
||||||
|
{
|
||||||
|
int nCharacter = pBuffer[nLocation + z];
|
||||||
|
if (nCharacter == 0)
|
||||||
|
break;
|
||||||
|
if ((nCharacter < 0x20) || (nCharacter > 0x7E))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// name
|
||||||
|
ASCIIEncoding ascii = new ASCIIEncoding();
|
||||||
|
int nNameCharacters = 0;
|
||||||
|
for ( nNameCharacters = nLocation; nNameCharacters < pBuffer.Length && pBuffer[nNameCharacters] != 0; nNameCharacters++)
|
||||||
|
;
|
||||||
|
string spName = ascii.GetString (pBuffer, nLocation, nNameCharacters-nLocation);
|
||||||
|
byte [] spFieldBuffer = new byte[nFieldValueSize];
|
||||||
|
|
||||||
|
nLocation = nNameCharacters + 1;
|
||||||
|
|
||||||
|
// value
|
||||||
|
Array.Copy(pBuffer, nLocation, spFieldBuffer, 0, nFieldValueSize);
|
||||||
|
nLocation += nFieldValueSize;
|
||||||
|
|
||||||
|
// set
|
||||||
|
SetFieldBinary(spName, spFieldBuffer, nFieldFlags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private void SortFields()
|
||||||
|
{
|
||||||
|
SortedList<byte[],string> list = new SortedList<byte[],string>();
|
||||||
|
}
|
||||||
|
// TODO private static int CompareFields(const void * pA, const void * pB);
|
||||||
|
|
||||||
|
// helper set / get field functions
|
||||||
|
//int SetFieldID3String(string pFieldName, const char * pFieldValue, int nBytes);
|
||||||
|
//int GetFieldID3String(string pFieldName, char * pBuffer, int nBytes);
|
||||||
|
|
||||||
|
// private data
|
||||||
|
private FileStream m_spIO;
|
||||||
|
private bool m_bAnalyzed;
|
||||||
|
private int m_nTagBytes;
|
||||||
|
private APETagField[] m_aryFields;
|
||||||
|
private bool m_bHasAPETag;
|
||||||
|
private int m_nAPETagVersion;
|
||||||
|
//private bool m_bHasID3Tag;
|
||||||
|
private bool m_bIgnoreReadOnly;
|
||||||
|
};
|
||||||
|
}
|
||||||
91
APETagDotNet/APETagDotNet.csproj
Normal file
91
APETagDotNet/APETagDotNet.csproj
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProductVersion>8.0.50727</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>{CA200BCB-DFC6-4153-9BD4-785BC768B26B}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>APETagDotNet</RootNamespace>
|
||||||
|
<AssemblyName>APETagDotNet</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>..\bin\Win32\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>..\bin\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>bin\win32\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
|
||||||
|
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||||
|
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||||
|
<OutputPath>bin\win32\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
|
||||||
|
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||||
|
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
|
||||||
|
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||||
|
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||||
|
<OutputPath>bin\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
|
||||||
|
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||||
|
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="APETagDotNet.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
35
APETagDotNet/Properties/AssemblyInfo.cs
Normal file
35
APETagDotNet/Properties/AssemblyInfo.cs
Normal file
@@ -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("APETagDotNet")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("APETagDotNet")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Matthew T. Ashland, Gregory S. Chudov")]
|
||||||
|
[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("d3dd931a-3cd8-4288-b171-ae9d6e51fed1")]
|
||||||
|
|
||||||
|
// 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")]
|
||||||
@@ -15,6 +15,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FLACDotNet", "..\FLACDotNet
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APEDotNet", "..\APEDotNet\APEDotNet.vcproj", "{9AE965C4-301E-4C01-B90F-297AF341ACC6}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APEDotNet", "..\APEDotNet\APEDotNet.vcproj", "{9AE965C4-301E-4C01-B90F-297AF341ACC6}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B} = {CA200BCB-DFC6-4153-9BD4-785BC768B26B}
|
||||||
{0B9C97D4-61B8-4294-A1DF-BA90752A1779} = {0B9C97D4-61B8-4294-A1DF-BA90752A1779}
|
{0B9C97D4-61B8-4294-A1DF-BA90752A1779} = {0B9C97D4-61B8-4294-A1DF-BA90752A1779}
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
@@ -22,7 +23,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodecLibs", "CodecLibs", "{
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WavPackDotNet", "..\WavPackDotNet\WavPackDotNet.vcproj", "{CC2E74B6-534A-43D8-9F16-AC03FE955000}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WavPackDotNet", "..\WavPackDotNet\WavPackDotNet.vcproj", "{CC2E74B6-534A-43D8-9F16-AC03FE955000}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
{0B9C97D4-61B8-4294-A1DF-BA90752A1779} = {0B9C97D4-61B8-4294-A1DF-BA90752A1779}
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B} = {CA200BCB-DFC6-4153-9BD4-785BC768B26B}
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MACLib", "..\MAC_SDK\Source\MACLib\MACLib.vcproj", "{0B9C97D4-61B8-4294-A1DF-BA90752A1779}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MACLib", "..\MAC_SDK\Source\MACLib\MACLib.vcproj", "{0B9C97D4-61B8-4294-A1DF-BA90752A1779}"
|
||||||
@@ -31,7 +32,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodecWrappers", "CodecWrapp
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libFLAC_static", "..\flac\src\libFLAC\libFLAC_static.vcproj", "{4CEFBC84-C215-11DB-8314-0800200C9A66}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libFLAC_static", "..\flac\src\libFLAC\libFLAC_static.vcproj", "{4CEFBC84-C215-11DB-8314-0800200C9A66}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APETagsDotNet", "..\APETagsDotNet\APETagsDotNet.vcproj", "{A8662B81-5B09-487E-B8F6-28251A8C46AF}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "APETagDotNet", "..\APETagDotNet\APETagDotNet.csproj", "{CA200BCB-DFC6-4153-9BD4-785BC768B26B}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -129,20 +130,22 @@ Global
|
|||||||
{4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x64.Build.0 = Release|x64
|
{4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x64.Build.0 = Release|x64
|
||||||
{4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x86.ActiveCfg = Release|Win32
|
{4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x86.ActiveCfg = Release|Win32
|
||||||
{4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x86.Build.0 = Release|Win32
|
{4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x86.Build.0 = Release|Win32
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Debug|Win32.ActiveCfg = Debug|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Debug|Win32.Build.0 = Debug|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Debug|Win32.ActiveCfg = Debug|x86
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Debug|x64.ActiveCfg = Debug|x64
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Debug|Win32.Build.0 = Debug|x86
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Debug|x64.Build.0 = Debug|x64
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Debug|x86.ActiveCfg = Debug|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Debug|x64.Build.0 = Debug|x64
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Debug|x86.Build.0 = Debug|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Release|Any CPU.ActiveCfg = Release|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Debug|x86.Build.0 = Debug|x86
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Release|Win32.ActiveCfg = Release|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Release|Win32.Build.0 = Release|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Release|x64.ActiveCfg = Release|x64
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Release|Win32.ActiveCfg = Release|x86
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Release|x64.Build.0 = Release|x64
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Release|Win32.Build.0 = Release|x86
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Release|x86.ActiveCfg = Release|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Release|x64.ActiveCfg = Release|x64
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF}.Release|x86.Build.0 = Release|Win32
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Release|x64.Build.0 = Release|x64
|
||||||
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{CA200BCB-DFC6-4153-9BD4-785BC768B26B}.Release|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -151,7 +154,6 @@ Global
|
|||||||
{9AE965C4-301E-4C01-B90F-297AF341ACC6} = {85F88959-C9E9-4989-ACB1-67BA9D1BEFE7}
|
{9AE965C4-301E-4C01-B90F-297AF341ACC6} = {85F88959-C9E9-4989-ACB1-67BA9D1BEFE7}
|
||||||
{CC2E74B6-534A-43D8-9F16-AC03FE955000} = {85F88959-C9E9-4989-ACB1-67BA9D1BEFE7}
|
{CC2E74B6-534A-43D8-9F16-AC03FE955000} = {85F88959-C9E9-4989-ACB1-67BA9D1BEFE7}
|
||||||
{E70FA90A-7012-4A52-86B5-362B699D1540} = {85F88959-C9E9-4989-ACB1-67BA9D1BEFE7}
|
{E70FA90A-7012-4A52-86B5-362B699D1540} = {85F88959-C9E9-4989-ACB1-67BA9D1BEFE7}
|
||||||
{A8662B81-5B09-487E-B8F6-28251A8C46AF} = {85F88959-C9E9-4989-ACB1-67BA9D1BEFE7}
|
|
||||||
{0B9C97D4-61B8-4294-A1DF-BA90752A1779} = {8B179853-B7D6-479C-B8B2-6CBCE835D040}
|
{0B9C97D4-61B8-4294-A1DF-BA90752A1779} = {8B179853-B7D6-479C-B8B2-6CBCE835D040}
|
||||||
{4CEFBC84-C215-11DB-8314-0800200C9A66} = {8B179853-B7D6-479C-B8B2-6CBCE835D040}
|
{4CEFBC84-C215-11DB-8314-0800200C9A66} = {8B179853-B7D6-479C-B8B2-6CBCE835D040}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -32,18 +32,19 @@
|
|||||||
using namespace System;
|
using namespace System;
|
||||||
using namespace System::Runtime::InteropServices;
|
using namespace System::Runtime::InteropServices;
|
||||||
using namespace System::Collections::Specialized;
|
using namespace System::Collections::Specialized;
|
||||||
|
using namespace APETagsDotNet;
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include "WavPack\wavpack.h"
|
#include "WavPack\wavpack.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
typedef char str_ansi;
|
//typedef char str_ansi;
|
||||||
typedef wchar_t str_utf16;
|
//typedef wchar_t str_utf16;
|
||||||
#define BOOL int
|
//#define BOOL int
|
||||||
#define TRUE 1
|
//#define TRUE 1
|
||||||
#define FALSE 0
|
//#define FALSE 0
|
||||||
#include "..\MAC_SDK\Shared\SmartPtr.h"
|
//#include "..\MAC_SDK\Shared\SmartPtr.h"
|
||||||
#include "..\MAC_SDK\Shared\APETag.h"
|
//#include "..\MAC_SDK\Shared\APETag.h"
|
||||||
|
|
||||||
namespace WavPackDotNet {
|
namespace WavPackDotNet {
|
||||||
int write_block(void *id, void *data, int32_t length);
|
int write_block(void *id, void *data, int32_t length);
|
||||||
@@ -54,15 +55,15 @@ namespace WavPackDotNet {
|
|||||||
IntPtr pathChars;
|
IntPtr pathChars;
|
||||||
char errorMessage[256];
|
char errorMessage[256];
|
||||||
|
|
||||||
_path = NULL;
|
_path = path;
|
||||||
|
|
||||||
pathChars = Marshal::StringToHGlobalUni(path);
|
pathChars = Marshal::StringToHGlobalUni(path);
|
||||||
size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1;
|
size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1;
|
||||||
_path = new wchar_t[pathLen];
|
wchar_t * pPath = new wchar_t[pathLen];
|
||||||
memcpy ((void*) _path, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t));
|
memcpy ((void*) pPath, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t));
|
||||||
Marshal::FreeHGlobal(pathChars);
|
Marshal::FreeHGlobal(pathChars);
|
||||||
|
|
||||||
_wpc = WavpackOpenFileInput(_path, errorMessage, OPEN_WVC, 0);
|
_wpc = WavpackOpenFileInput(pPath, errorMessage, OPEN_WVC, 0);
|
||||||
if (_wpc == NULL) {
|
if (_wpc == NULL) {
|
||||||
throw gcnew Exception("Unable to initialize the decoder.");
|
throw gcnew Exception("Unable to initialize the decoder.");
|
||||||
}
|
}
|
||||||
@@ -76,7 +77,6 @@ namespace WavPackDotNet {
|
|||||||
|
|
||||||
~WavPackReader()
|
~WavPackReader()
|
||||||
{
|
{
|
||||||
if (_path) delete [] _path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
property Int32 BitsPerSample {
|
property Int32 BitsPerSample {
|
||||||
@@ -123,7 +123,7 @@ namespace WavPackDotNet {
|
|||||||
|
|
||||||
property NameValueCollection^ Tags {
|
property NameValueCollection^ Tags {
|
||||||
NameValueCollection^ get () {
|
NameValueCollection^ get () {
|
||||||
if (!_tags) GetTags ();
|
if (!_tags) _tags = (gcnew APETagDotNet (_path, true))->GetStringTags (true);
|
||||||
return _tags;
|
return _tags;
|
||||||
}
|
}
|
||||||
void set (NameValueCollection ^tags) {
|
void set (NameValueCollection ^tags) {
|
||||||
@@ -152,32 +152,7 @@ namespace WavPackDotNet {
|
|||||||
NameValueCollection^ _tags;
|
NameValueCollection^ _tags;
|
||||||
Int32 _sampleCount, _sampleOffset;
|
Int32 _sampleCount, _sampleOffset;
|
||||||
Int32 _bitsPerSample, _channelCount, _sampleRate;
|
Int32 _bitsPerSample, _channelCount, _sampleRate;
|
||||||
const wchar_t * _path;
|
String^ _path;
|
||||||
|
|
||||||
void GetTags (void)
|
|
||||||
{
|
|
||||||
_tags = gcnew NameValueCollection();
|
|
||||||
CAPETag apeTag (_path, TRUE);
|
|
||||||
for (int i = 0; ; i++)
|
|
||||||
{
|
|
||||||
CAPETagField * field = apeTag.GetTagField (i);
|
|
||||||
if (!field)
|
|
||||||
break;
|
|
||||||
if (field->GetIsUTF8Text())
|
|
||||||
{
|
|
||||||
int valueSize = field->GetFieldValueSize();
|
|
||||||
while (valueSize && field->GetFieldValue()[valueSize-1]=='\0')
|
|
||||||
valueSize --;
|
|
||||||
const wchar_t * fieldName = field->GetFieldName ();
|
|
||||||
if (!_wcsicmp (fieldName, L"YEAR"))
|
|
||||||
fieldName = L"DATE";
|
|
||||||
if (!_wcsicmp (fieldName, L"TRACK"))
|
|
||||||
fieldName = L"TRACKNUMBER";
|
|
||||||
_tags->Add (gcnew String (fieldName),
|
|
||||||
gcnew String (field->GetFieldValue(), 0, valueSize, System::Text::Encoding::UTF8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public ref class WavPackWriter {
|
public ref class WavPackWriter {
|
||||||
|
|||||||
@@ -115,6 +115,7 @@
|
|||||||
/>
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCLCompilerTool"
|
Name="VCCLCompilerTool"
|
||||||
|
AdditionalUsingDirectories=""
|
||||||
PreprocessorDefinitions="WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS"
|
PreprocessorDefinitions="WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS"
|
||||||
RuntimeLibrary="2"
|
RuntimeLibrary="2"
|
||||||
UsePrecompiledHeader="0"
|
UsePrecompiledHeader="0"
|
||||||
@@ -134,6 +135,7 @@
|
|||||||
Name="VCLinkerTool"
|
Name="VCLinkerTool"
|
||||||
AdditionalDependencies="WavPack\libwavpack.lib $(NOINHERIT)"
|
AdditionalDependencies="WavPack\libwavpack.lib $(NOINHERIT)"
|
||||||
LinkIncremental="1"
|
LinkIncremental="1"
|
||||||
|
AdditionalLibraryDirectories=""
|
||||||
GenerateDebugInformation="true"
|
GenerateDebugInformation="true"
|
||||||
TargetMachine="1"
|
TargetMachine="1"
|
||||||
/>
|
/>
|
||||||
@@ -176,6 +178,10 @@
|
|||||||
RelativePath="System.XML.dll"
|
RelativePath="System.XML.dll"
|
||||||
AssemblyName="System.Xml, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
|
AssemblyName="System.Xml, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
|
||||||
/>
|
/>
|
||||||
|
<ProjectReference
|
||||||
|
ReferencedProjectIdentifier="{CA200BCB-DFC6-4153-9BD4-785BC768B26B}"
|
||||||
|
RelativePathToProject="..\APETagDotNet\APETagDotNet.csproj"
|
||||||
|
/>
|
||||||
</References>
|
</References>
|
||||||
<Files>
|
<Files>
|
||||||
<Filter
|
<Filter
|
||||||
|
|||||||
Reference in New Issue
Block a user