Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

30
Src/apev2/apev2.sln Normal file
View File

@ -0,0 +1,30 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29424.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "apev2", "apev2.vcxproj", "{48387E27-2666-4ACF-9C21-AE508C529580}"
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
{48387E27-2666-4ACF-9C21-AE508C529580}.Debug|Win32.ActiveCfg = Debug|Win32
{48387E27-2666-4ACF-9C21-AE508C529580}.Debug|Win32.Build.0 = Debug|Win32
{48387E27-2666-4ACF-9C21-AE508C529580}.Release|Win32.ActiveCfg = Release|Win32
{48387E27-2666-4ACF-9C21-AE508C529580}.Release|Win32.Build.0 = Release|Win32
{48387E27-2666-4ACF-9C21-AE508C529580}.Debug|x64.ActiveCfg = Debug|x64
{48387E27-2666-4ACF-9C21-AE508C529580}.Debug|x64.Build.0 = Debug|x64
{48387E27-2666-4ACF-9C21-AE508C529580}.Release|x64.ActiveCfg = Release|x64
{48387E27-2666-4ACF-9C21-AE508C529580}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5A83B542-4B24-4F95-B752-B5D7567B4F54}
EndGlobalSection
EndGlobal

195
Src/apev2/apev2.vcxproj Normal file
View File

@ -0,0 +1,195 @@
<?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>{48387E27-2666-4ACF-9C21-AE508C529580}</ProjectGuid>
<RootNamespace>apev2</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
</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>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<IncludePath>$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<IncludePath>$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<VcpkgConfiguration>Debug</VcpkgConfiguration>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<VcpkgConfiguration>Debug</VcpkgConfiguration>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;../Wasabi;../replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;../Wasabi;../replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MinSpace</Optimization>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>../;../Wasabi;../replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<ForcedIncludeFiles>tag.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<Optimization>MinSpace</Optimization>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>../;../Wasabi;../replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<ForcedIncludeFiles>tag.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="flags.h" />
<ClInclude Include="header.h" />
<ClInclude Include="item.h" />
<ClInclude Include="tag.h" />
<ClInclude Include="util.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="header.cpp" />
<ClCompile Include="item.cpp" />
<ClCompile Include="tag.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="header.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="item.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tag.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="flags.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="header.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="item.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="tag.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{6cf90466-6c89-4be4-83c3-e7872a927708}</UniqueIdentifier>
</Filter>
<Filter Include="Ressource Files">
<UniqueIdentifier>{eaaa2f1a-68ef-4456-8bfa-47b0c6183c31}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{6cd9a1b4-b2a9-412e-a920-a163ab8e3cf5}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

40
Src/apev2/flags.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef NULLSOFT_APEV2_FLAGS_H
#define NULLSOFT_APEV2_FLAGS_H
namespace APEv2
{
enum
{
APEV2_SUCCESS = 0,
APEV2_FAILURE = 1,
APEV2_TOO_SMALL = 2,
APEV2_KEY_NOT_FOUND=3,
APEV2_NO_DATA = 4, /* Key found, but data is empty or corrupt */
APEV2_END_OF_ITEMS=5,
};
/*
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,
/* item specific flags */
MASK_ITEM_TYPE = ((1 << 2) | (1 << 1)),
FLAG_ITEM_TEXT = 0,
FLAG_ITEM_BINARY = (1 << 1), /* We compare WITHOUT SHIFTING so all flag values are << 1 */
FLAG_ITEM_LOCATOR = (2 << 1),
FLAG_ITEM_RESERVED = (3 << 1),
};
}
#endif

88
Src/apev2/header.cpp Normal file
View File

@ -0,0 +1,88 @@
#include "header.h"
#include "flags.h"
#include "util.h"
#include <limits.h>
static char preamble[] = { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' };
APEv2::Header::Header(void *data)
{
memcpy(&headerData, data, sizeof(headerData));
// covert to host endian
//headerData.preamble=htons(headerData.preamble);
headerData.version=ATON16(headerData.version);
headerData.size=ATON32(headerData.size);
headerData.items=ATON32(headerData.items);
headerData.flags=ATON32(headerData.flags);
}
APEv2::Header::Header(const HeaderData *data)
{
memcpy(&headerData.preamble, preamble, sizeof(headerData.preamble));
headerData.version = NTOA32(2000);
headerData.size = data->size;
headerData.items = data->items;
headerData.flags = data->flags;
headerData.reserved = 0;
}
uint32_t APEv2::Header::GetFlags()
{
return headerData.flags;
}
bool APEv2::Header::Valid()
{
return !memcmp(&headerData.preamble, preamble, 8) && headerData.reserved == 0;
}
uint32_t APEv2::Header::TagSize()
{
size_t size = headerData.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()
{
return !!(headerData.flags & FLAG_HEADER_HAS_HEADER);
}
bool APEv2::Header::HasFooter()
{
return !(headerData.flags & FLAG_HEADER_NO_FOOTER);
}
bool APEv2::Header::IsFooter()
{
return !(headerData.flags & FLAG_HEADER_IS_HEADER);
}
bool APEv2::Header::IsHeader()
{
return !!(headerData.flags & FLAG_HEADER_IS_HEADER);
}
int APEv2::Header::Encode(void *data, size_t len)
{
if (len < 32)
return APEV2_TOO_SMALL;
HeaderData endianCorrectData = headerData;
endianCorrectData.version=NTOA16(endianCorrectData.version);
endianCorrectData.size=NTOA32(endianCorrectData.size);
endianCorrectData.items=NTOA32(endianCorrectData.items);
endianCorrectData.flags=NTOA32(endianCorrectData.flags);
memcpy(data, &endianCorrectData, sizeof(endianCorrectData));
return APEV2_SUCCESS;
}

44
Src/apev2/header.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef NULLSOFT_APEV2_HEADER_H
#define NULLSOFT_APEV2_HEADER_H
#include <bfc/platform/types.h>
namespace APEv2
{
#pragma pack(push, 1)
struct HeaderData
{
uint64_t preamble;
uint32_t version;
uint32_t size;
uint32_t items;
uint32_t flags;
uint16_t reserved;
};
#pragma pack(pop)
class Header
{
public:
Header(void *data);
Header(const HeaderData *data);
bool Valid();
uint32_t TagSize();
bool HasHeader();
bool HasFooter();
bool IsFooter();
bool IsHeader();
int Encode(void *data, size_t len);
uint32_t GetFlags();
enum
{
SIZE=32,
};
private:
HeaderData headerData;
};
}
#endif

221
Src/apev2/item.cpp Normal file
View File

@ -0,0 +1,221 @@
#include "item.h"
#include "flags.h"
#include "util.h"
#include <strsafe.h>
#include <stdint.h>
/*
http://wiki.hydrogenaudio.org/index.php?title=APE_Tag_Item
*/
APEv2::Item::Item()
{
refCount=1;
len=0;
flags=0;
key=0;
value=0;
}
APEv2::Item::~Item()
{
free(key);
free(value);
}
void APEv2::Item::Retain()
{
refCount++;
}
void APEv2::Item::Release()
{
if (--refCount == 0)
delete this;
}
int APEv2::Item::Read(void *_data, size_t datalen, void **new_data, size_t *new_len)
{
char *data = (char *)_data;
if (datalen < 4)
return APEV2_TOO_SMALL;
memcpy(&len, data, 4);
len = ATON32(len);
data+=4;
datalen-=4;
if (datalen < 4)
return APEV2_TOO_SMALL;
memcpy(&flags, data, 4);
flags = ATON32(flags);
data+=4;
datalen-=4;
uint32_t key_len=0;
for (uint32_t i=0;i<datalen;i++)
{
if (data[i] == 0)
{
key_len=i;
break;
}
}
if (key_len == datalen)
return APEV2_TOO_SMALL;
if (key_len == 0)
return APEV2_FAILURE;
if (key)
{
free(key);
key = 0;
}
key = (char *)calloc(key_len+1, sizeof(char));
if (key)
{
StringCchCopyA(key, key_len+1, data);
datalen-=(key_len+1);
data+=(key_len+1);
if (datalen < len)
{
free(key);
key = 0;
return APEV2_TOO_SMALL;
}
if (value)
{
free(value);
value = 0;
}
value = (char *)calloc(len, sizeof(char));
if (value)
{
memcpy(value, data, len);
datalen-=len;
data+=len;
*new_len = datalen;
*new_data=data;
return APEV2_SUCCESS;
}
else
{
free(key);
return APEV2_FAILURE;
}
}
else
return APEV2_FAILURE;
}
bool APEv2::Item::IsReadOnly()
{
return flags & FLAG_READONLY;
}
bool APEv2::Item::IsString()
{
return (flags & MASK_ITEM_TYPE) == FLAG_ITEM_TEXT;
}
bool APEv2::Item::KeyMatch(const char *key_to_compare, int compare)
{
if (!key || !*key)
return false;
switch (compare)
{
case ITEM_KEY_COMPARE_CASE_INSENSITIVE:
return !_stricmp(key_to_compare, key);
case ITEM_KEY_COMPARE_CASE_SENSITIVE:
return !strcmp(key_to_compare, key);
default:
return false;
}
}
int APEv2::Item::Get(void **data, size_t *datalen)
{
if (!value || !len)
return APEV2_FAILURE;
*data = value;
*datalen = len;
return APEV2_SUCCESS;
}
int APEv2::Item::Set(const void *data, size_t datalen, int dataType)
{
if (!data || !datalen)
return APEV2_FAILURE;
// set data type for this item
flags &= ~MASK_ITEM_TYPE;
flags |= dataType;
free(value);
value = malloc(datalen);
len=(uint32_t)datalen;
memcpy(value, data, len);
return APEV2_SUCCESS;
}
int APEv2::Item::SetKey(const char *tag)
{
if (!tag || !*tag)
return APEV2_FAILURE;
free(key);
key = _strdup(tag);
return APEV2_SUCCESS;
}
int APEv2::Item::GetKey(const char **tag)
{
if (!key)
return APEV2_FAILURE;
*tag = key;
return APEV2_SUCCESS;
}
size_t APEv2::Item::EncodeSize()
{
return 4 /* size */ + 4 /* flags */ + (key && *key ? strlen(key) : 0) + 1 /* NULL separator */ + len;
}
int APEv2::Item::Encode(void *data, size_t datalen)
{
if (!key || !value || !len)
return APEV2_FAILURE;
if (datalen < EncodeSize())
return APEV2_TOO_SMALL;
int8_t *ptr = (int8_t *)data;
// write data length
int32_t _len = NTOA32(len);
memcpy(ptr, &_len, sizeof(_len));
ptr+=sizeof(_len);
datalen-=sizeof(_len);
// write flags
int32_t _flags = NTOA32(flags);
memcpy(ptr, &_flags, sizeof(_flags));
ptr+=sizeof(_flags);
datalen-=sizeof(_flags);
// write key and null terminator
if (StringCchCopyExA((char *)ptr, datalen, key, (char **) &ptr, &datalen, 0) != S_OK)
return APEV2_FAILURE;
// account for null separator
ptr++;
datalen--;
// write data
memcpy(ptr, value, len);
return APEV2_SUCCESS;
}

43
Src/apev2/item.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef NULLSOFT_APEV2_ITEM_H
#define NULLSOFT_APEV2_ITEM_H
#include <bfc/platform/types.h>
namespace APEv2
{
enum
{
ITEM_KEY_COMPARE_CASE_INSENSITIVE = 0,
ITEM_KEY_COMPARE_CASE_SENSITIVE = 1,
};
class Item
{
public:
Item();
~Item();
void Retain();
void Release();
/* If successful, puts incremented data pointer in new_data, and new data size remaining in new_len */
int Read(void *data, size_t len, void **new_data, size_t *new_len);
int Encode(void *data, size_t len);
size_t EncodeSize();
bool IsReadOnly();
bool IsString();
bool KeyMatch(const char *key, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE);
int Get(void **data, size_t *len);
int GetKey(const char **tag);
int Set(const void *data, size_t len, int dataType);
int SetKey(const char *tag);
private:
size_t refCount;
uint32_t flags;
char *key;
void *value;
uint32_t len;
};
}
#endif

353
Src/apev2/tag.cpp Normal file
View File

@ -0,0 +1,353 @@
#include "tag.h"
#include "header.h"
#include "flags.h"
#include "nu/ns_wc.h"
#include <limits.h>
#include <strsafe.h>
#include <stdint.h>
/*
http://wiki.hydrogenaudio.org/index.php?title=APEv2_specification
*/
APEv2::Tag::Tag()
{
flags = 0; // default to writing just a footer
}
int APEv2::Tag::Parse(const void *_data, size_t len)
{
char *data = (char *)_data;
if (len < Header::SIZE)
return APEV2_TOO_SMALL;
char *headerStart = data+(len-Header::SIZE);
Header footer(headerStart);
if (footer.Valid()
&& !(len == Header::SIZE && footer.IsHeader())) // if all we have is this footer, we should check that it's not really a header
{
len -= Header::SIZE;
char *dataStart = data;
if (footer.HasHeader())
{
// TODO: validate header
dataStart+= Header::SIZE;
len-=Header::SIZE;
}
flags = footer.GetFlags();
flags &= ~FLAG_HEADER_NO_FOOTER; // winamp 5.54 had this flag reversed, so let's correct it
return ParseData(dataStart, len);
}
// ok, we didn't have a footer, so let's see if we have a header
headerStart = data;
Header header(headerStart);
if (header.Valid())
{
len -= Header::SIZE;
char *dataStart = data + Header::SIZE;
if (header.HasFooter())
{
// TODO: validate footer
// benski> cut... we got here because we didn't have a footer.
//len-=Header::SIZE;
}
flags = header.GetFlags();
flags |= FLAG_HEADER_NO_FOOTER; // winamp 5.54 had this flag reversed, so let's correct it
return ParseData(dataStart, len);
}
return APEV2_FAILURE;
}
int APEv2::Tag::ParseData(const void *data, size_t len)
{
char *dataStart = (char *)data;
while (len)
{
Item *item = new Item;
size_t new_len = 0;
void *new_data = 0;
int ret = item->Read(dataStart, len, &new_data, &new_len);
if (ret == APEV2_SUCCESS)
{
len = new_len;
dataStart = (char *)new_data;
items.push_back(item);
}
else
{
item->Release();
return ret;
}
}
return APEV2_SUCCESS;
}
int APEv2::Tag::GetString(const char *tag, wchar_t *data, size_t dataLen)
{
/* our UTF-8 to wchar_t conversion function wants an int for size, so we should explicitly check the size */
if (dataLen > INT_MAX)
return APEV2_FAILURE;
for ( APEv2::Item *l_item : items )
{
/* check if it's a string first, and then match the key next (will be faster) */
if ( l_item->IsString() && l_item->KeyMatch(tag, ITEM_KEY_COMPARE_CASE_INSENSITIVE))
{
void *item_data = 0;
size_t item_dataLen = 0;
if ( l_item->Get(&item_data, &item_dataLen) == APEV2_SUCCESS && item_dataLen < INT_MAX)
{
int signed_len = static_cast<int>(item_dataLen); // we checked against INT_MAX above so this conversion is safe
if (MultiByteToWideCharSZ(CP_UTF8, 0, (LPCSTR)item_data, signed_len, data, (int)dataLen) != 0)
return APEV2_SUCCESS;
}
return APEV2_NO_DATA;
}
}
return APEV2_KEY_NOT_FOUND;
}
int APEv2::Tag::SetItemData(APEv2::Item *item, const wchar_t *data)
{
int utf16_data_cch = (int)wcslen(data);
int item_dataLen = WideCharToMultiByte(CP_UTF8, 0, data, utf16_data_cch, 0, 0, NULL, NULL);
if (!item_dataLen)
return APEV2_FAILURE;
char *item_data = (char *)calloc(item_dataLen, sizeof(char));
if (item_data)
{
if (!WideCharToMultiByte(CP_UTF8, 0, data, utf16_data_cch, item_data, item_dataLen, NULL, NULL))
{
free(item_data);
return APEV2_FAILURE;
}
item->Set(item_data, item_dataLen, FLAG_ITEM_TEXT);
free(item_data);
return APEV2_SUCCESS;
}
return APEV2_FAILURE;
}
int APEv2::Tag::SetString(const char *tag, const wchar_t *data)
{
for (auto it = items.begin(); it != items.end(); it++)
{
APEv2::Item* item = *it;
if (item->KeyMatch(tag, ITEM_KEY_COMPARE_CASE_INSENSITIVE))
{
if (data && *data)
{
return SetItemData(item, data);
}
else
{
item->Release();
it = items.erase(it);
return APEV2_SUCCESS;
}
}
}
if (data && *data)
{
/* Not already in the list, so we need to add it */
Item *newItem = new Item;
if (newItem->SetKey(tag) == APEV2_SUCCESS && SetItemData(newItem, data) == APEV2_SUCCESS)
{
items.push_back(newItem);
}
else
{
newItem->Release();
return APEV2_FAILURE;
}
}
return APEV2_SUCCESS;
}
int APEv2::Tag::EnumValue(size_t i, const char **tag, wchar_t *data, size_t dataLen)
{
/* our UTF-8 to wchar_t conversion function wants an int for size, so we should explicitly check the size */
if (dataLen > INT_MAX)
return APEV2_FAILURE;
if (i >= items.size())
return APEV2_END_OF_ITEMS;
APEv2::Item *item = items[i];
item->GetKey(tag);
if (!data || !dataLen) // if they don't want the data, go ahead and return now
return APEV2_SUCCESS;
data[0]=0;
void *item_data = 0;
size_t item_dataLen = 0;
if (item->IsString())
{
if (item->Get(&item_data, &item_dataLen) == APEV2_SUCCESS && item_dataLen < INT_MAX)
{
int signed_len = static_cast<int>(item_dataLen); // we checked against INT_MAX above so this conversion is safe
if (MultiByteToWideCharSZ(CP_UTF8, 0, (LPCSTR)item_data, signed_len, data, (int)dataLen) == 0)
*data=0;
}
}
else
{
// TODO: benski> hmmm
StringCchCopyW(data, dataLen, L"[TODO: deal with binary]");
}
return APEV2_SUCCESS;
}
void APEv2::Tag::Clear()
{
for ( APEv2::Item *l_item : items )
{
l_item->Release();
}
items.clear();
}
int APEv2::Tag::RemoveItem(size_t index)
{
if (index >= items.size())
return APEV2_END_OF_ITEMS;
APEv2::Item *item = items[index];
items.erase(items.begin() + index);
item->Release();
return APEV2_SUCCESS;
}
size_t APEv2::Tag::EncodeSize()
{
size_t totalSize=0;
if (flags & FLAG_HEADER_HAS_HEADER)
totalSize+=Header::SIZE;
for ( APEv2::Item *l_item : items )
{
totalSize += l_item->EncodeSize();
}
if (!(flags & FLAG_HEADER_NO_FOOTER))
totalSize+=Header::SIZE;
return totalSize;
}
int APEv2::Tag::Encode(void *data, size_t len)
{
size_t totalSize=0;
int8_t *ptr = (int8_t *)data;
if (flags & FLAG_HEADER_HAS_HEADER)
{
HeaderData headerData = {0};
headerData.size= (uint32_t)totalSize;
if (!(flags & FLAG_HEADER_NO_FOOTER))
headerData.size += Header::SIZE;
headerData.items = (uint32_t)items.size();
headerData.flags = (flags & FLAG_HEADER_ENCODE_MASK)|FLAG_HEADER_IS_HEADER;
Header header(&headerData);
int ret = header.Encode(ptr, len);
if (ret != APEV2_SUCCESS)
return ret;
ptr += Header::SIZE;
len -= Header::SIZE;
}
for ( APEv2::Item *l_item : items )
{
int ret = l_item->Encode(ptr, len);
if (ret!= APEV2_SUCCESS)
return ret;
size_t itemSize = l_item->EncodeSize();
len-=itemSize;
ptr+=itemSize;
totalSize+=itemSize;
}
if (!(flags & FLAG_HEADER_NO_FOOTER))
{
HeaderData footerData = {0};
footerData.size= (uint32_t)totalSize + Header::SIZE;
footerData.items = (uint32_t)items.size();
footerData.flags = (flags & FLAG_HEADER_ENCODE_MASK);
Header footer(&footerData);
int ret = footer.Encode(ptr, len);
if (ret != APEV2_SUCCESS)
return ret;
}
return APEV2_SUCCESS;
}
int APEv2::Tag::SetKeyValueByIndex(size_t index, const char *key, const wchar_t *data)
{
if (index >= items.size())
return APEV2_END_OF_ITEMS;
// TODO: check for duplicate key
items[index]->SetKey(key);
return SetItemData(items[index], data);
}
APEv2::Item *APEv2::Tag::AddItem()
{
Item *newItem = new Item;
items.push_back(newItem);
return newItem;
}
int APEv2::Tag::SetFlags(uint32_t newflags, uint32_t mask)
{
flags = (flags & ~mask) | newflags;
return APEV2_SUCCESS;
}
int APEv2::Tag::FindItemByKey(const char *key, size_t *index, int compare)
{
for (size_t i=0;i!=items.size();i++)
{
if (items[i]->KeyMatch(key, compare))
{
*index = i;
return APEV2_SUCCESS;
}
}
return APEV2_KEY_NOT_FOUND;
}
size_t APEv2::Tag::GetNumItems()
{
return items.size();
}
bool APEv2::Tag::IsReadOnly()
{
return flags & FLAG_READONLY;
}
bool APEv2::Tag::IsItemReadOnly(size_t index)
{
if (index >= items.size())
return true;
return items[index]->IsReadOnly();
}

55
Src/apev2/tag.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef NULLSOFT_APEV2_TAG_H
#define NULLSOFT_APEV2_TAG_H
#include <bfc/platform/types.h>
#include <vector>
#include "item.h"
#include "flags.h"
namespace APEv2
{
class Tag
{
public:
Tag();
/* Parsing */
int Parse(const void *data, size_t len);
/* Retrieving Data */
int GetString(const char *tag, wchar_t *data, size_t dataLen);
int EnumValue(size_t i, const char **tag, wchar_t *data, size_t dataLen);
int FindItemByKey(const char *key, size_t *index, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE);
size_t GetNumItems();
bool IsReadOnly();
bool IsItemReadOnly(size_t index);
/* Setting Data */
Item *AddItem();
int SetString(const char *tag, const wchar_t *data);
int SetKeyValueByIndex(size_t index, const char *key, const wchar_t *data);
int SetFlags(uint32_t flags, uint32_t mask);
/* Removing Data */
void Clear();
int RemoveItem(size_t index);
int RemoveItem(const char *tag);
/* Serializing */
size_t EncodeSize();
int Encode(void *data, size_t len);
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. */
protected:
int SetItemData(APEv2::Item *item, const wchar_t *data);
private: /* data */
std::vector<APEv2::Item*> items;
uint32_t flags;
};
}
#endif

22
Src/apev2/util.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef NULLSOFT_APEV2_UTIL_H
#define NULLSOFT_APEV2_UTIL_H
#if defined(BIG_ENDIAN)
#define ATON16(A) ((((uint16_t)(A) & 0xff00) >> 8) | \
(((uint16_t)(A) & 0x00ff) << 8))
#define ATON32(A) ((((uint32_t)(A) & 0xff000000) >> 25) | \
(((uint32_t)(A) & 0x00ff0000) >> 8) | \
(((uint32_t)(A) & 0x0000ff00) << 8) | \
(((uint32_t)(A) & 0x000000ff) << 24))
#elif defined(LITTLE_ENDIAN) || defined(_M_IX86) || defined(_M_X64) || defined(_WIN64)
#define ATON16(A) (A)
#define ATON32(A) (A)
#else
#error neither BIG_ENDIAN nor LITTLE_ENDIAN defined!
#endif
#define NTOA32(N) ATON32(N)
#define NTOA16(N) ATON16(N)
#endif