diff options
Diffstat (limited to 'Src/replicant/nsapev2')
-rw-r--r-- | Src/replicant/nsapev2/VERSION | 1 | ||||
-rw-r--r-- | Src/replicant/nsapev2/flags.h | 20 | ||||
-rw-r--r-- | Src/replicant/nsapev2/header.cpp | 91 | ||||
-rw-r--r-- | Src/replicant/nsapev2/header.h | 39 | ||||
-rw-r--r-- | Src/replicant/nsapev2/item.cpp | 235 | ||||
-rw-r--r-- | Src/replicant/nsapev2/item.h | 43 | ||||
-rw-r--r-- | Src/replicant/nsapev2/nsapev2.h | 75 | ||||
-rw-r--r-- | Src/replicant/nsapev2/nsapev2.sln | 81 | ||||
-rw-r--r-- | Src/replicant/nsapev2/nsapev2.vcxproj | 172 | ||||
-rw-r--r-- | Src/replicant/nsapev2/nsapev2_common.cpp | 465 | ||||
-rw-r--r-- | Src/replicant/nsapev2/precomp.h | 26 | ||||
-rw-r--r-- | Src/replicant/nsapev2/tag.cpp | 285 | ||||
-rw-r--r-- | Src/replicant/nsapev2/tag.h | 50 | ||||
-rw-r--r-- | Src/replicant/nsapev2/util.h | 1 | ||||
-rw-r--r-- | Src/replicant/nsapev2/windows/nsapev2.cpp | 1 | ||||
-rw-r--r-- | Src/replicant/nsapev2/windows/nsapev2.h | 20 |
16 files changed, 1605 insertions, 0 deletions
diff --git a/Src/replicant/nsapev2/VERSION b/Src/replicant/nsapev2/VERSION new file mode 100644 index 00000000..ea710abb --- /dev/null +++ b/Src/replicant/nsapev2/VERSION @@ -0,0 +1 @@ +1.2
\ No newline at end of file diff --git a/Src/replicant/nsapev2/flags.h b/Src/replicant/nsapev2/flags.h new file mode 100644 index 00000000..621857f1 --- /dev/null +++ b/Src/replicant/nsapev2/flags.h @@ -0,0 +1,20 @@ +#pragma once +#include "foundation/error.h" +namespace APEv2 +{ + /* + http://wiki.hydrogenaudio.org/index.php?title=Ape_Tags_Flags + */ + enum + { + /* flags for header or item */ + FLAG_READONLY = 1, + + /* header/footer specific flags */ + FLAG_HEADER_HAS_HEADER = (1 << 31), + FLAG_HEADER_NO_FOOTER = (1 << 30), + FLAG_HEADER_IS_HEADER = (1 << 29), + FLAG_HEADER_ENCODE_MASK = FLAG_READONLY|FLAG_HEADER_HAS_HEADER|FLAG_HEADER_NO_FOOTER, + }; + +} diff --git a/Src/replicant/nsapev2/header.cpp b/Src/replicant/nsapev2/header.cpp new file mode 100644 index 00000000..eee96b9a --- /dev/null +++ b/Src/replicant/nsapev2/header.cpp @@ -0,0 +1,91 @@ +#include "header.h" +#include "flags.h" +#include "util.h" +#include "nu/ByteReader.h" +#include "nu/ByteWriter.h" +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +static char apev2_preamble[] = { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' }; +APEv2::Header::Header() +{ + memcpy(preamble, apev2_preamble, 8); + version=2000; + size=0; + items=0; + flags=0; + reserved=0; +} + +APEv2::Header::Header(const void *data) +{ + bytereader_value_t byte_reader; + bytereader_init(&byte_reader, data, Header::SIZE); + + bytereader_read_n(&byte_reader, preamble, 8); + version=bytereader_read_u32_le(&byte_reader); + size=bytereader_read_u32_le(&byte_reader); + items=bytereader_read_u32_le(&byte_reader); + flags=bytereader_read_u32_le(&byte_reader); + bytereader_read_n(&byte_reader, &reserved, 8); +} + +uint32_t APEv2::Header::GetFlags() const +{ + return flags; +} + +bool APEv2::Header::Valid() const +{ + return !memcmp(preamble, apev2_preamble, 8) && reserved == 0; +} + +uint32_t APEv2::Header::TagSize() const +{ + size_t size = this->size; + if (IsHeader() && HasFooter()) + size+=SIZE; + if (IsFooter() && HasHeader()) + size+=SIZE; + + if (size > ULONG_MAX) + return 0; + + return (uint32_t)size; +} + +bool APEv2::Header::HasHeader() const +{ + return !!(flags & FLAG_HEADER_HAS_HEADER); +} + +bool APEv2::Header::HasFooter() const +{ + return !(flags & FLAG_HEADER_NO_FOOTER); +} + +bool APEv2::Header::IsFooter() const +{ + return !(flags & FLAG_HEADER_IS_HEADER); +} + +bool APEv2::Header::IsHeader() const +{ + return !!(flags & FLAG_HEADER_IS_HEADER); +} + +int APEv2::Header::Encode(bytewriter_t byte_writer) const +{ + if (bytewriter_size(byte_writer) < 32) + return NErr_Insufficient; + + bytewriter_write_n(byte_writer, apev2_preamble, 8); + bytewriter_write_u32_le(byte_writer, version); + bytewriter_write_u32_le(byte_writer, size); + bytewriter_write_u32_le(byte_writer, items); + bytewriter_write_u32_le(byte_writer, flags); + bytewriter_write_zero_n(byte_writer, 8); + + return NErr_Success; +}
\ No newline at end of file diff --git a/Src/replicant/nsapev2/header.h b/Src/replicant/nsapev2/header.h new file mode 100644 index 00000000..462722e1 --- /dev/null +++ b/Src/replicant/nsapev2/header.h @@ -0,0 +1,39 @@ +#pragma once +#include "foundation/types.h" +#include "nu/ByteWriter.h" +class nstest; + +namespace APEv2 +{ + class Header + { + friend class ::nstest; + public: + Header(); + Header(const void *data); + void SetSize(uint32_t size) { this->size = size; } + void SetFlags(uint32_t flags) { this->flags = flags; } + void SetItems(uint32_t items) { this->items = items; } + bool Valid() const; + uint32_t TagSize() const; + bool HasHeader() const; + bool HasFooter() const; + bool IsFooter() const; + bool IsHeader() const; + int Encode(bytewriter_t byte_writer) const; + uint32_t GetFlags() const; + enum + { + SIZE=32, + }; + private: + uint8_t preamble[8]; + uint32_t version; + uint32_t size; + uint32_t items; + uint32_t flags; + uint64_t reserved; + }; +} + + diff --git a/Src/replicant/nsapev2/item.cpp b/Src/replicant/nsapev2/item.cpp new file mode 100644 index 00000000..2d5184ea --- /dev/null +++ b/Src/replicant/nsapev2/item.cpp @@ -0,0 +1,235 @@ +#include "item.h" +#include "flags.h" +#include "util.h" +#include "nu/ByteWriter.h" +#include "nu/strsafe.h" +#include "nu/ByteReader.h" +#include "nsapev2/nsapev2.h" +#include <stdlib.h> + +/* +http://wiki.hydrogenaudio.org/index.php?title=APE_Tag_Item + +Item layout: +[0-3] length of value field (little endian) +[4-7] flags (little endian) +[null terminated] key +[length] value +*/ + +APEv2::Item::Item() +{ + len=0; + flags=0; + key=0; + value=0; +} + +APEv2::Item::~Item() +{ + free(key); + free(value); +} + +int APEv2::Item::Read(bytereader_t byte_reader) +{ + if (bytereader_size(byte_reader) < 8) + return NErr_NeedMoreData; + + /* read fixed-size fields */ + len = bytereader_read_u32_le(byte_reader); + flags = bytereader_read_u32_le(byte_reader); + + /* find the null terminator */ + size_t key_len = bytereader_find_zero(byte_reader); + + /* make sure we didn't hit the end of our buffer */ + if (key_len == bytereader_size(byte_reader)) + return NErr_Insufficient; + + /* check for empty key and also check for integer overflow */ + if (key_len == 0 || key_len+1 == 0) + return NErr_Error; + + key = (char *)malloc(key_len+1); + if (key) + { + bytereader_read_n(byte_reader, key, key_len+1); /* read key and terminator*/ + + if (bytereader_size(byte_reader) < len) /* make sure we have room for the value! */ + { + free(key); + key=0; + return NErr_NeedMoreData; + } + + value = (char *)malloc(len); + if (value) + { + bytereader_read_n(byte_reader, value, len); /* read value */ + return NErr_Success; + } + else + { + free(key); + key=0; + return NErr_OutOfMemory; + } + } + else + return NErr_OutOfMemory; + +} + +bool APEv2::Item::IsReadOnly() +{ + return flags & FLAG_READONLY; +} + +bool APEv2::Item::KeyMatch(const char *key_to_compare, int compare) +{ + if (!key || !*key) + return false; + + switch (compare) + { + case ITEM_KEY_COMPARE_CASE_INSENSITIVE: +#ifdef _WIN32 + return !_stricmp(key_to_compare, key); +#else + return !strcasecmp(key_to_compare, key); +#endif + case ITEM_KEY_COMPARE_CASE_SENSITIVE: + return !strcmp(key_to_compare, key); + default: + return false; + } +} + +int APEv2::Item::Get(const void **data, size_t *datalen) const +{ + if (!value || !len) + return NErr_Empty; + *data = value; + *datalen = len; + return NErr_Success; +} + +int APEv2::Item::Set(nx_string_t string) +{ + if (!value) + return NErr_BadParameter; + + flags &= ~nsapev2_item_type_mask; + flags |= nsapev2_item_type_utf8; + + size_t bytes; + int ret = NXStringGetBytesSize(&bytes, string, nx_charset_utf8, 0); + if (ret != NErr_DirectPointer && ret != NErr_Success) + return ret; + + void *new_value = malloc(bytes); + if (!new_value) + return NErr_OutOfMemory; + + size_t bytes_copied; + ret = NXStringGetBytes(&bytes_copied, string, new_value, bytes, nx_charset_utf8, 0); + if (ret != NErr_Success) + { + free(new_value); + return ret; + } + + free(value); + value=new_value; + len=(uint32_t)bytes_copied; + return NErr_Success; +} + +int APEv2::Item::Set(const void *data, size_t datalen, int data_type) +{ + if (!data || !datalen) + return NErr_Error; + + // set data type for this item + flags &= ~nsapev2_item_type_mask; + flags |= data_type; + + void *new_value = realloc(value, datalen); + if (!new_value) + return NErr_OutOfMemory; + + value=new_value; + len=(uint32_t)datalen; + memcpy(value, data, len); + return NErr_Success; +} + + int APEv2::Item::New(size_t datalen, int data_type, void **bytes) + { + if (!datalen) + return NErr_Error; + + // set data type for this item + flags &= ~nsapev2_item_type_mask; + flags |= data_type; + + void *new_value = realloc(value, datalen); + if (!new_value) + return NErr_OutOfMemory; + + value=new_value; + + len=(uint32_t)datalen; + *bytes = value; + return NErr_Success; + } + +int APEv2::Item::SetKey(const char *tag) +{ + if (!tag || !*tag) + return NErr_Error; + + char *new_key = strdup(tag); + if (!new_key) + return NErr_OutOfMemory; + + free(key); + key = new_key; + return NErr_Success; +} + +int APEv2::Item::GetKey(const char **tag) const +{ + if (!key) + return NErr_Error; + *tag = key; + return NErr_Success; +} + +size_t APEv2::Item::EncodeSize() const +{ + return 4 /* size */ + 4 /* flags */ + strlen(key) + 1 /* NULL separator */ + len; +} + +uint32_t APEv2::Item::GetFlags() const +{ + return flags; +} + +int APEv2::Item::Encode(bytewriter_t byte_writer) const +{ + if (!key || !value || !len) + return NErr_Error; + + if (bytewriter_size(byte_writer) < EncodeSize()) + return NErr_Insufficient; + + bytewriter_write_u32_le(byte_writer, len); + bytewriter_write_u32_le(byte_writer, flags); + bytewriter_write_n(byte_writer, key, strlen(key) + 1); + bytewriter_write_n(byte_writer, value, len); + + return NErr_Success; +} + diff --git a/Src/replicant/nsapev2/item.h b/Src/replicant/nsapev2/item.h new file mode 100644 index 00000000..de07d472 --- /dev/null +++ b/Src/replicant/nsapev2/item.h @@ -0,0 +1,43 @@ +#pragma once +#include "foundation/types.h" +#include "nx/nxstring.h" +#include "nu/PtrDeque.h" +#include "nu/ByteWriter.h" +#include "nu/ByteReader.h" +namespace APEv2 +{ + enum + { + ITEM_KEY_COMPARE_CASE_INSENSITIVE = 0, + ITEM_KEY_COMPARE_CASE_SENSITIVE = 1, + }; + + class Item : public nu::PtrDequeNode + { + public: + Item(); + ~Item(); + + /* If successful, puts incremented data pointer in new_data, and new data size remaining in new_len */ + int Read(bytereader_t byte_reader); + + int Encode(bytewriter_t byte_writer) const; + size_t EncodeSize() const; + + bool IsReadOnly(); + bool KeyMatch(const char *key, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE); + int Get(const void **data, size_t *len) const; + int GetKey(const char **tag) const; + int Set(nx_string_t value); + int Set(const void *data, size_t len, int dataType); + int SetKey(const char *tag); + int New(size_t datalen, int data_type, void **bytes); + uint32_t GetFlags() const; + + private: + uint32_t flags; + char *key; + void *value; + uint32_t len; + }; +} diff --git a/Src/replicant/nsapev2/nsapev2.h b/Src/replicant/nsapev2/nsapev2.h new file mode 100644 index 00000000..1b703d1e --- /dev/null +++ b/Src/replicant/nsapev2/nsapev2.h @@ -0,0 +1,75 @@ +#pragma once + +#include "foundation/types.h" +#include "nx/nxstring.h" + +#ifdef __ANDROID__ +#include "android/nsapev2.h" +#elif defined(_WIN32) +#include "windows/nsapev2.h" +#elif defined(__linux__) +#include "linux/nsapev2.h" +#elif defined (__APPLE__) +#include "osx/nsapev2.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct nsapev2_header_struct_t { } *nsapev2_header_t; + typedef struct nsapev2_tag_struct_t { } *nsapev2_tag_t; + typedef struct nsapev2_item_struct_t { } *nsapev2_item_t; + + // must be exactly 32 bytes + NSAPEV2_EXPORT int NSAPEv2_Header_Valid(const void *header_data); + NSAPEV2_EXPORT int NSAPEv2_Header_Create(nsapev2_header_t *header, const void *header_data, size_t header_len); + NSAPEV2_EXPORT int NSAPEv2_Header_TagSize(const nsapev2_header_t header, uint32_t *tag_size); + NSAPEV2_EXPORT int NSAPEv2_Header_HasHeader(nsapev2_header_t header); + NSAPEV2_EXPORT int NSAPEv2_Header_Destroy(nsapev2_header_t header); + + + // currently, this function makes a copy of any necessary data. in the future, it would be better + // to make another version of this function that "borrows" your data + // if you can guarantee that the memory will outlive the nsapev2_tag_t object + NSAPEV2_EXPORT int NSAPEv2_Tag_Create(nsapev2_tag_t *tag, const nsapev2_header_t header, const void *bytes, size_t bytes_len); + NSAPEV2_EXPORT int NSAPEv2_Tag_New(nsapev2_tag_t *tag); + NSAPEV2_EXPORT int NSAPEv2_Tag_SerializedSize(const nsapev2_tag_t tag, size_t *bytes); + NSAPEV2_EXPORT int NSAPEv2_Tag_Serialize(const nsapev2_tag_t t, void *data, size_t bytes); + NSAPEV2_EXPORT int NSAPEv2_Tag_Destroy(nsapev2_tag_t tag); + + NSAPEV2_EXPORT int NSAPEv2_Tag_GetFlags(nsapev2_tag_t tag, uint32_t *flags); + NSAPEV2_EXPORT int NSAPEv2_Tag_SetFlags(nsapev2_tag_t tag, uint32_t flags, uint32_t mask); + + NSAPEV2_EXPORT int NSAPEv2_Tag_GetString(const nsapev2_tag_t tag, const char *key, unsigned int index, nx_string_t *value); + NSAPEV2_EXPORT int NSAPEv2_Tag_SetString(nsapev2_tag_t tag, const char *key, unsigned int index, nx_string_t value); + + NSAPEV2_EXPORT int NSAPEv2_Tag_GetBinary(const nsapev2_tag_t tag, const char *key, unsigned int index, const void **bytes, size_t *length); + NSAPEV2_EXPORT int NSAPEv2_Tag_SetBinary(nsapev2_tag_t tag, const char *key, unsigned int index, const void *bytes, size_t length); + NSAPEV2_EXPORT int NSAPEv2_Tag_SetArtwork(nsapev2_tag_t t, const char *key, unsigned int index, const char *filename, const void *bytes, size_t length); + /* Items */ + // flags + static const uint32_t nsapev2_item_read_write = 0; + static const uint32_t nsapev2_item_read_only = 1; + + static const uint32_t nsapev2_item_type_utf8 = 0; + static const uint32_t nsapev2_item_type_binary = 1 << 1; + static const uint32_t nsapev2_item_type_locator = 2 << 1; + + // mask flags with these to check the different fields + static const uint32_t nsapev2_item_type_mask = (1 << 2) | (1 << 1); + static const uint32_t nsapev2_item_read_mask = 1 << 0; + + NSAPEV2_EXPORT int NSAPEv2_Tag_EnumerateItems(const nsapev2_tag_t tag, const nsapev2_item_t start, nsapev2_item_t *next); + NSAPEV2_EXPORT int NSAPEv2_Tag_GetItemCount(const nsapev2_tag_t t, size_t *count); + NSAPEV2_EXPORT int NSAPEv2_Tag_GetItemAtIndex(const nsapev2_tag_t t, unsigned int index, nsapev2_item_t *item); + NSAPEV2_EXPORT int NSAPEv2_Item_GetInformation(const nsapev2_item_t item, const char **key, uint32_t *flags); + NSAPEV2_EXPORT int NSAPEv2_Item_GetString(const nsapev2_item_t item, nx_string_t *value); + NSAPEV2_EXPORT int NSAPEv2_Item_GetBinary(const nsapev2_item_t item, const void **bytes, size_t *length); + NSAPEV2_EXPORT int NSAPEv2_Item_SetKey(nsapev2_item_t item, const char *key); + NSAPEV2_EXPORT int NSAPEv2_Item_SetString(nsapev2_item_t item, nx_string_t value); + NSAPEV2_EXPORT int NSAPEv2_Tag_RemoveItem(nsapev2_tag_t tag, nsapev2_item_t item); + NSAPEV2_EXPORT int NSAPEv2_Tag_AddItem(nsapev2_tag_t tag, nsapev2_item_t *item); +#ifdef __cplusplus +} +#endif diff --git a/Src/replicant/nsapev2/nsapev2.sln b/Src/replicant/nsapev2/nsapev2.sln new file mode 100644 index 00000000..10f0a861 --- /dev/null +++ b/Src/replicant/nsapev2/nsapev2.sln @@ -0,0 +1,81 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsapev2", "nsapev2.vcxproj", "{BF92BB16-012A-43F4-BC67-30D6D5187598}" + ProjectSection(ProjectDependencies) = postProject + {57C90706-B25D-4ACA-9B33-95CDB2427C27} = {57C90706-B25D-4ACA-9B33-95CDB2427C27} + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} + {E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D} + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}" + ProjectSection(ProjectDependencies) = postProject + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}" + ProjectSection(ProjectDependencies) = postProject + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BF92BB16-012A-43F4-BC67-30D6D5187598}.Debug|Win32.ActiveCfg = Debug|Win32 + {BF92BB16-012A-43F4-BC67-30D6D5187598}.Debug|Win32.Build.0 = Debug|Win32 + {BF92BB16-012A-43F4-BC67-30D6D5187598}.Debug|x64.ActiveCfg = Debug|x64 + {BF92BB16-012A-43F4-BC67-30D6D5187598}.Debug|x64.Build.0 = Debug|x64 + {BF92BB16-012A-43F4-BC67-30D6D5187598}.Release|Win32.ActiveCfg = Release|Win32 + {BF92BB16-012A-43F4-BC67-30D6D5187598}.Release|Win32.Build.0 = Release|Win32 + {BF92BB16-012A-43F4-BC67-30D6D5187598}.Release|x64.ActiveCfg = Release|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE} + EndGlobalSection +EndGlobal diff --git a/Src/replicant/nsapev2/nsapev2.vcxproj b/Src/replicant/nsapev2/nsapev2.vcxproj new file mode 100644 index 00000000..cf514039 --- /dev/null +++ b/Src/replicant/nsapev2/nsapev2.vcxproj @@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{BF92BB16-012A-43F4-BC67-30D6D5187598}</ProjectGuid> + <RootNamespace>nsapev2</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>x86_Debug\</OutDir> + <IntDir>x86_Debug\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>x64_Debug\</OutDir> + <IntDir>x64_Debug\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>x86_Release\</OutDir> + <IntDir>x86_Release\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>x64_Release\</OutDir> + <IntDir>x64_Release\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>EditAndContinue</DebugInformationFormat> + <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Lib> + <OutputFile>$(ProjectDir)x86_Debug\$(ProjectName).lib</OutputFile> + </Lib> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>EditAndContinue</DebugInformationFormat> + <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Lib> + <OutputFile>$(ProjectDir)x64_Debug\$(ProjectName).lib</OutputFile> + </Lib> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <IntrinsicFunctions>true</IntrinsicFunctions> + <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Lib> + <OutputFile>$(ProjectDir)x86_Release\$(ProjectName).lib</OutputFile> + </Lib> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <IntrinsicFunctions>true</IntrinsicFunctions> + <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Lib> + <OutputFile>$(ProjectDir)x64_Release\$(ProjectName).lib</OutputFile> + </Lib> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="header.cpp" /> + <ClCompile Include="item.cpp" /> + <ClCompile Include="nsapev2_common.cpp" /> + <ClCompile Include="tag.cpp" /> + <ClCompile Include="windows\nsapev2.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="flags.h" /> + <ClInclude Include="header.h" /> + <ClInclude Include="item.h" /> + <ClInclude Include="nsapev2.h" /> + <ClInclude Include="precomp.h" /> + <ClInclude Include="tag.h" /> + <ClInclude Include="util.h" /> + <ClInclude Include="windows\nsapev2.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/replicant/nsapev2/nsapev2_common.cpp b/Src/replicant/nsapev2/nsapev2_common.cpp new file mode 100644 index 00000000..badcb100 --- /dev/null +++ b/Src/replicant/nsapev2/nsapev2_common.cpp @@ -0,0 +1,465 @@ +#include "nsapev2.h" +#include "nsapev2/header.h" +#include "nsapev2/tag.h" +#include <new> + +int NSAPEv2_Header_Valid(const void *header_data) + +{ + if (APEv2::Header(header_data).Valid()) + return NErr_Success; + else + return NErr_False; +} + +int NSAPEv2_Header_Create(nsapev2_header_t *header, const void *header_data, size_t header_len) +{ + if (header_len < 10 || NSAPEv2_Header_Valid(header_data) != NErr_Success) + return NErr_Error; + + nsapev2_header_t new_header = (nsapev2_header_t)new (std::nothrow) APEv2::Header(header_data); + if (!new_header) + return NErr_OutOfMemory; + *header = new_header; + return NErr_Success; +} + +int NSAPEv2_Header_TagSize(const nsapev2_header_t h, uint32_t *tag_size) +{ + const APEv2::Header *header = (const APEv2::Header *)h; + if (!header) + return NErr_NullPointer; + + *tag_size = header->TagSize(); + return NErr_Success; +} + +int NSAPEv2_Header_HasHeader(nsapev2_header_t h) +{ + const APEv2::Header *header = (const APEv2::Header *)h; + if (!header) + return NErr_NullPointer; + + if (header->HasHeader()) + return NErr_Success; + else + return NErr_False; +} + +int NSAPEv2_Header_Destroy(nsapev2_header_t h) +{ + const APEv2::Header *header = (const APEv2::Header *)h; + if (!header) + return NErr_NullPointer; + + delete header; + return NErr_Success; +} + +int NSAPEv2_Tag_Create(nsapev2_tag_t *t, const nsapev2_header_t h, const void *bytes, size_t bytes_len) +{ + const APEv2::Header *header = (const APEv2::Header *)h; + if (!header) + return NErr_NullPointer; + + APEv2::Tag *tag = new (std::nothrow) APEv2::Tag; + if (!tag) + return NErr_OutOfMemory; + + int ret = tag->Parse(header, bytes, bytes_len); + if (ret != NErr_Success) + { + delete tag; + return ret; + } + + *t = (nsapev2_tag_t)tag; + return ret; +} + +int NSAPEv2_Tag_New(nsapev2_tag_t *t) +{ + APEv2::Header *new_header = new (std::nothrow) APEv2::Header(); + if (!new_header) + return NErr_OutOfMemory; + + APEv2::Tag *tag = new (std::nothrow) APEv2::Tag; + if (!tag) + { + delete new_header; + return NErr_OutOfMemory; + } + + *t = (nsapev2_tag_t)tag; + return NErr_Success; +} + +int NSAPEv2_Tag_SerializedSize(const nsapev2_tag_t t, size_t *bytes) +{ + const APEv2::Tag *tag = (const APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + *bytes = tag->EncodeSize(); + return NErr_Success; +} + +int NSAPEv2_Tag_Serialize(const nsapev2_tag_t t, void *data, size_t bytes) +{ + const APEv2::Tag *tag = (const APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + return tag->Encode(data, bytes); +} + +int NSAPEv2_Tag_Destroy(nsapev2_tag_t t) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_NullPointer; + delete tag; + return NErr_Success; +} + +int NSAPEv2_Tag_GetFlags(nsapev2_tag_t t, uint32_t *flags) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + return tag->GetFlags(flags); +} + +int NSAPEv2_Tag_SetFlags(nsapev2_tag_t t, uint32_t flags, uint32_t mask) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + return tag->SetFlags(flags, mask); +} + +int NSAPEv2_Tag_GetString(const nsapev2_tag_t t, const char *key, unsigned int index, nx_string_t *value) +{ + const APEv2::Tag *tag = (const APEv2::Tag *)t; + if (!tag) + return NErr_Empty; + + const void *data; + size_t data_len; + int ret = tag->GetData(key, index, &data, &data_len); + if (ret != NErr_Success) + return ret; + + return NXStringCreateWithBytes(value, data, data_len, nx_charset_utf8); +} + +int NSAPEv2_Tag_SetString(nsapev2_tag_t t, const char *key, unsigned int index, nx_string_t value) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + if (value) + { + APEv2::Item *item; + int ret = tag->GetItem(key, index, &item); + if (ret == NErr_Success) + { + return item->Set(value); + } + else + { + /* no such item, need to make a new one */ + item = new (std::nothrow) APEv2::Item; + if (!item) + return NErr_OutOfMemory; + + ret = item->SetKey(key); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + ret = item->Set(value); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + ret = tag->AddItem(item); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + return NErr_Success; + } + } + else + { + tag->Remove(key, index); + return NErr_Success; + } +} + +int NSAPEv2_Tag_GetBinary(const nsapev2_tag_t t, const char *key, unsigned int index, const void **bytes, size_t *length) +{ + const APEv2::Tag *tag = (const APEv2::Tag *)t; + if (!tag) + return NErr_Empty; + + return tag->GetData(key, index, bytes, length); +} + +int NSAPEv2_Tag_SetBinary(nsapev2_tag_t t, const char *key, unsigned int index, const void *bytes, size_t length) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + if (bytes) + { + APEv2::Item *item; + int ret = tag->GetItem(key, index, &item); + if (ret == NErr_Success) + { + return item->Set(bytes, length, nsapev2_item_type_binary); + } + else + { + /* no such item, need to make a new one */ + item = new (std::nothrow) APEv2::Item; + if (!item) + return NErr_OutOfMemory; + + ret = item->SetKey(key); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + ret = item->Set(bytes, length, nsapev2_item_type_binary); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + ret = tag->AddItem(item); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + return NErr_Success; + } + } + else + { + tag->Remove(key, index); + return NErr_Success; + } +} + +static int SetArtwork(APEv2::Item *item, const char *filename, const void *bytes, size_t length) +{ + + size_t filename_length = filename?strlen(filename):0; + size_t total_length = filename_length + + 1 /* null separator */ + + length; + + void *item_data; + int ret = item->New(total_length, nsapev2_item_type_binary, &item_data); + if (ret != NErr_Success) + return ret; + + bytewriter_s byte_writer; + bytewriter_init(&byte_writer, item_data, total_length); + bytewriter_write_n(&byte_writer, filename, filename_length); + bytewriter_write_u8(&byte_writer, 0); + bytewriter_write_n(&byte_writer, bytes, length); + + return NErr_Success; +} + +int NSAPEv2_Tag_SetArtwork(nsapev2_tag_t t, const char *key, unsigned int index, const char *filename, const void *bytes, size_t length) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + if (bytes) + { + + APEv2::Item *item; + int ret = tag->GetItem(key, index, &item); + if (ret == NErr_Success) + { + return SetArtwork(item, filename, bytes, length); + } + else + { + /* no such item, need to make a new one */ + item = new (std::nothrow) APEv2::Item; + if (!item) + return NErr_OutOfMemory; + + ret = item->SetKey(key); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + ret = SetArtwork(item, filename, bytes, length); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + ret = tag->AddItem(item); + if (ret != NErr_Success) + { + delete item; + return ret; + } + + return NErr_Success; + } + } + else + { + tag->Remove(key, index); + return NErr_Success; + } +} + +int NSAPEv2_Tag_EnumerateItems(const nsapev2_tag_t t, const nsapev2_item_t s, nsapev2_item_t *next) +{ + const APEv2::Tag *tag = (const APEv2::Tag *)t; + if (!tag) + return NErr_NullPointer; + + const APEv2::Item *start = (const APEv2::Item *)s; + return tag->EnumerateItems(start, (APEv2::Item **)next); +} + +int NSAPEv2_Tag_GetItemCount(const nsapev2_tag_t t, size_t *count) +{ + const APEv2::Tag *tag = (const APEv2::Tag *)t; + if (!tag) + return NErr_NullPointer; + + return tag->GetItemCount(count); +} + + +int NSAPEv2_Tag_GetItemAtIndex(const nsapev2_tag_t t, unsigned int index, nsapev2_item_t *item) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_NullPointer; + + return tag->GetItemAtIndex(index, (APEv2::Item **)item); +} + +int NSAPEv2_Item_GetInformation(const nsapev2_item_t i, const char **key, uint32_t *flags) +{ + const APEv2::Item *item = (const APEv2::Item *)i; + if (!item) + return NErr_NullPointer; + + if (key) + { + int ret = item->GetKey(key); + if (ret != NErr_Success) + return ret; + } + + if (flags) + *flags = item->GetFlags(); + return NErr_Success; + +} + +int NSAPEv2_Item_GetString(const nsapev2_item_t i, nx_string_t *value) +{ + const APEv2::Item *item = (const APEv2::Item *)i; + if (!item) + return NErr_BadParameter; + + const void *data; + size_t data_len; + int ret = item->Get(&data, &data_len); + if (ret != NErr_Success) + return ret; + + return NXStringCreateWithBytes(value, data, data_len, nx_charset_utf8); +} + +int NSAPEv2_Item_GetBinary(const nsapev2_item_t i, const void **bytes, size_t *length) +{ + const APEv2::Item *item = (const APEv2::Item *)i; + if (!item) + return NErr_BadParameter; + + return item->Get(bytes, length); +} + +int NSAPEv2_Item_SetKey(nsapev2_item_t i, const char *key) +{ + APEv2::Item *item = (APEv2::Item *)i; + if (!item) + return NErr_BadParameter; + + return item->SetKey(key); +} + +int NSAPEv2_Item_SetString(nsapev2_item_t i, nx_string_t value) +{ + APEv2::Item *item = (APEv2::Item *)i; + if (!item) + return NErr_BadParameter; + + return item->Set(value); +} + +int NSAPEv2_Tag_RemoveItem(nsapev2_tag_t t, nsapev2_item_t i) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + APEv2::Item *item = (APEv2::Item *)i; + if (!item) + return NErr_BadParameter; + + tag->RemoveItem(item); + return NErr_Success; +} + +int NSAPEv2_Tag_AddItem(nsapev2_tag_t t, nsapev2_item_t *i) +{ + APEv2::Tag *tag = (APEv2::Tag *)t; + if (!tag) + return NErr_BadParameter; + + APEv2::Item **item = (APEv2::Item **)i; + if (!item) + return NErr_NullPointer; + + *item = new (std::nothrow) APEv2::Item; + if (!*item) + return NErr_OutOfMemory; + + return tag->AddItem(*item); +}
\ No newline at end of file diff --git a/Src/replicant/nsapev2/precomp.h b/Src/replicant/nsapev2/precomp.h new file mode 100644 index 00000000..5d7ef44d --- /dev/null +++ b/Src/replicant/nsapev2/precomp.h @@ -0,0 +1,26 @@ +// +// precomp.h +// nsapev2 +// + +#include "foundation/error.h" +#include "foundation/types.h" + +#include "nu/ByteReader.h" +#include "nu/ByteWriter.h" +#include "nu/strsafe.h" + +#ifdef __cplusplus +#include "nu/PtrDeque.h" +#endif + +#include "nx/nxstring.h" + +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __cplusplus +#include "header.h" +#include "new" +#endif diff --git a/Src/replicant/nsapev2/tag.cpp b/Src/replicant/nsapev2/tag.cpp new file mode 100644 index 00000000..fcaeb091 --- /dev/null +++ b/Src/replicant/nsapev2/tag.cpp @@ -0,0 +1,285 @@ +#include "tag.h" +#include "header.h" +#include "flags.h" +#ifdef _WIN32 +#include "../nu/ns_wc.h" +#include "nu/strsafe.h" +#endif +#include <limits.h> + +#include <new> +#include "nu/ByteWriter.h" + +/* +http://wiki.hydrogenaudio.org/index.php?title=APEv2_specification +*/ + +APEv2::Tag::Tag() +{ + flags = 0; // default to writing just a footer +} + +APEv2::Tag::~Tag() +{ + items.deleteAll(); +} + +/* Parsing */ +int APEv2::Tag::Parse(const APEv2::Header *header, const void *_data, size_t len) +{ + flags = header->GetFlags(); + + if (header->IsFooter()) + { + flags &= ~FLAG_HEADER_NO_FOOTER; // winamp 5.54 had this flag reversed, so let's correct it + if (header->HasHeader()) + { + // TODO: validate header + _data = (const uint8_t *)_data + 32; + len -= 32; + } + len -= 32; /* footer is counted in the size */ + return ParseData(_data, len); + } + else /* IsHeader() */ + { + if (header->HasFooter()) + { + // TODO: validate footer + len -= 32; + } + return ParseData(_data, len); + } +} + +int APEv2::Tag::ParseData(const void *data, size_t len) +{ + bytereader_s byte_reader; + bytereader_init(&byte_reader, data, len); + + while (bytereader_size(&byte_reader)) + { + Item *item = new (std::nothrow) Item; + if (!item) + return NErr_OutOfMemory; + + int ret = item->Read(&byte_reader); + if (ret == NErr_Success) + { + items.push_back(item); + } + else + { + delete item; + return ret; + } + } + return NErr_Success; +} + +/* Retrieving Data */ +int APEv2::Tag::GetItem(const char *key, unsigned int index, Item **item, int compare) const +{ + unsigned int i=0; + for (ItemList::iterator itr=items.begin();itr!=items.end();itr++) + { + /* check if it's a string first, and then match the key next (will be faster) */ + if (itr->KeyMatch(key, compare)) + { + if (i++ < index) + continue; + + *item = *itr; + return NErr_Success; + } + } + + if (i > index) /* if we found the key once, but the index was too high */ + return NErr_EndOfEnumeration; + return NErr_Empty; +} + +int APEv2::Tag::GetItemAtIndex(unsigned int index, Item **item) const +{ + unsigned int i=0; + for (ItemList::iterator itr=items.begin();itr!=items.end();itr++) + { + if (i++ < index) + continue; + + *item = *itr; + return NErr_Success; + } + + return NErr_EndOfEnumeration; +} + +int APEv2::Tag::GetData(const char *key, unsigned int index, const void **data, size_t *data_len, int compare) const +{ + Item *item=0; + int ret = GetItem(key, index, &item, compare); + if (ret != NErr_Success) + return ret; + + return item->Get(data, data_len); +} + +int APEv2::Tag::EnumerateItems(const Item *start, Item **item) const +{ + Item *next_item = 0; + if (!start) + { + next_item = items.front(); + } + else + { + next_item = static_cast<APEv2::Item *>(start->next); + } + *item = next_item; + if (next_item) + return NErr_Success; + else if (start) + return NErr_EndOfEnumeration; + else + return NErr_Empty; +} + +int APEv2::Tag::FindItemByKey(const char *key, Item **item, int compare) const +{ + for (ItemList::iterator itr=items.begin();itr!=items.end();itr++) + { + if (itr->KeyMatch(key, compare)) + { + *item = *itr; + return NErr_Success; + } + } + return NErr_Unknown; +} + +bool APEv2::Tag::IsReadOnly() const +{ + return flags & FLAG_READONLY; +} + +int APEv2::Tag::GetItemCount(size_t *count) const +{ + *count = items.size(); + return NErr_Success; +} + +int APEv2::Tag::GetFlags(uint32_t *flags) const +{ + *flags = this->flags; + return NErr_Success; +} + +/* Setting Data */ +int APEv2::Tag::AddItem(APEv2::Item *new_item) +{ + items.push_back(new_item); + return NErr_Success; +} + +int APEv2::Tag::SetFlags(uint32_t newflags, uint32_t mask) +{ + flags = (flags & ~mask) | newflags; + return NErr_Success; +} + +/* Removing Data */ +void APEv2::Tag::Clear() +{ + items.deleteAll(); +} + +void APEv2::Tag::Remove(const char *key, unsigned int starting_index, int compare) +{ + for (ItemList::iterator itr=items.begin();itr!=items.end();) + { + ItemList::iterator next = itr; + next++; + APEv2::Item *item = *itr; + + if (item->KeyMatch(key, compare)) + { + if (starting_index) + { + starting_index--; + } + else + { + items.erase(item); + delete item; + } + } + itr=next; + } +} + +void APEv2::Tag::RemoveItem(Item *item) +{ + items.erase(item); + delete item; +} + +/* Serializing */ +size_t APEv2::Tag::EncodeSize() const +{ + size_t total_size=0; + + if (flags & FLAG_HEADER_HAS_HEADER) + total_size+=Header::SIZE; + + for (ItemList::iterator itr=items.begin();itr!=items.end();itr++) + { + total_size += itr->EncodeSize(); + } + + if (!(flags & FLAG_HEADER_NO_FOOTER)) + total_size+=Header::SIZE; + + return total_size; +} + +int APEv2::Tag::Encode(void *data, size_t len) const +{ + bytewriter_s byte_writer; + bytewriter_init(&byte_writer, data, len); + + if (flags & FLAG_HEADER_HAS_HEADER) + { + Header header; + header.SetSize((uint32_t)len - Header::SIZE); + header.SetItems((uint32_t)items.size()); + header.SetFlags((flags & FLAG_HEADER_ENCODE_MASK)|FLAG_HEADER_IS_HEADER); + + int ret = header.Encode(&byte_writer); + if (ret != NErr_Success) + return ret; + } + + for (ItemList::iterator itr=items.begin();itr!=items.end();itr++) + { + int ret = itr->Encode(&byte_writer); + if (ret!= NErr_Success) + return ret; + } + + if (!(flags & FLAG_HEADER_NO_FOOTER)) + { + Header footer; + + if (flags & FLAG_HEADER_HAS_HEADER) + footer.SetSize((uint32_t)len - Header::SIZE); + else + footer.SetSize((uint32_t)len); + footer.SetItems((uint32_t)items.size()); + footer.SetFlags((flags & FLAG_HEADER_ENCODE_MASK)); + + int ret = footer.Encode(&byte_writer); + if (ret != NErr_Success) + return ret; + } + return NErr_Success; +} diff --git a/Src/replicant/nsapev2/tag.h b/Src/replicant/nsapev2/tag.h new file mode 100644 index 00000000..a43c0499 --- /dev/null +++ b/Src/replicant/nsapev2/tag.h @@ -0,0 +1,50 @@ +#pragma once +#include "foundation/types.h" +#include "nu/PtrDeque.h" +#include "item.h" +#include "flags.h" +#include "header.h" + +namespace APEv2 +{ +class Tag +{ +public: + Tag(); + ~Tag(); + + /* Parsing */ + int Parse(const APEv2::Header *header, const void *data, size_t len); + + /* Retrieving Data */ + int GetItem(const char *key, unsigned int index, Item **item, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE) const; + int GetItemAtIndex(unsigned int index, Item **) const; + int GetData(const char *tag, unsigned int index, const void **data, size_t *data_len, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE) const; + int EnumerateItems(const Item *start, Item **item) const; + int FindItemByKey(const char *key, Item **item, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE) const; + bool IsReadOnly() const; + int GetItemCount(size_t *count) const; + int GetFlags(uint32_t *flags) const; + + /* Setting Data */ + int AddItem(APEv2::Item *new_item); + int SetFlags(uint32_t flags, uint32_t mask); + + /* Removing Data */ + void Clear(); + void Remove(const char *key, unsigned int starting_index, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE); // removes all items matching a key, but skips the first 'starting_index' items + void RemoveItem(Item *item); + /* Serializing */ + size_t EncodeSize() const; + int Encode(void *data, size_t len) const; + +private: /* methods */ + int ParseData(const void *data, size_t len); /* helper function, call with data pointing to beginning of items block (skip header), and length without footer. */ + +private: /* data */ + typedef nu::PtrDeque<Item> ItemList; + ItemList items; + uint32_t flags; +}; +} + diff --git a/Src/replicant/nsapev2/util.h b/Src/replicant/nsapev2/util.h new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/Src/replicant/nsapev2/util.h @@ -0,0 +1 @@ +#pragma once diff --git a/Src/replicant/nsapev2/windows/nsapev2.cpp b/Src/replicant/nsapev2/windows/nsapev2.cpp new file mode 100644 index 00000000..b8d4ee0c --- /dev/null +++ b/Src/replicant/nsapev2/windows/nsapev2.cpp @@ -0,0 +1 @@ +#include "nsapev2/nsapev2.h" diff --git a/Src/replicant/nsapev2/windows/nsapev2.h b/Src/replicant/nsapev2/windows/nsapev2.h new file mode 100644 index 00000000..cdf0c17c --- /dev/null +++ b/Src/replicant/nsapev2/windows/nsapev2.h @@ -0,0 +1,20 @@ +#pragma once + +#include "foundation/export.h" + +#ifdef __cplusplus +extern "C" { +#endif +/* +#ifdef NSID3V2_EXPORTS +#define NSAPEV2_EXPORT __declspec(dllexport) +#else +#define NSAPEV2_EXPORT __declspec(dllimport) +#endif +*/ +#define NSAPEV2_EXPORT + + +#ifdef __cplusplus +} +#endif
\ No newline at end of file |