winamp/Src/replicant/nsid3v2/frame_utils.cpp
2024-09-24 14:54:57 +02:00

265 lines
6.4 KiB
C++

#include "frame_utils.h"
#include "foundation/error.h"
#include "nsid3v2/nsid3v2.h"
#if defined(_WIN32) && !defined(strcasecmp)
#define strcasecmp _stricmp
#else
#include <string.h>
#endif
int ParseDescription(const char *&str, size_t &data_len, size_t &str_cch)
{
str_cch=0;
while (data_len && str[str_cch])
{
data_len--;
str_cch++;
}
if (!data_len)
return NErr_Error;
data_len--;
return NErr_Success;
}
int ParseDescription(const wchar_t *&str, size_t &data_len, size_t &str_cch, uint8_t &str_encoding)
{
str_cch=0;
if (data_len > 2 && str[0] == 0xFFFE)
{
str_encoding=2;
str++;
str-=3;
}
else if (data_len > 2 && str[0] == 0xFEFF)
{
str_encoding=1;
str++;
data_len-=3;
}
else
{
data_len--;
}
while (data_len > 1 && str[str_cch])
{
data_len-=2;
str_cch++;
}
if (!data_len)
return NErr_Error;
data_len-=2;
return NErr_Success;
}
static void ParseBOM(bytereader_t reader, uint8_t *encoding, const uint8_t default_encoding)
{
if (bytereader_size(reader) >= 2)
{
uint16_t bom = bytereader_show_u16_le(reader);
if (bom == 0xFFFE)
{
bytereader_advance(reader, 2);
*encoding=2;
}
else if (bom == 0xFEFF)
{
bytereader_advance(reader, 2);
*encoding=1;
}
else
{
*encoding=default_encoding;
}
}
else
{
*encoding=default_encoding;
}
}
int ParseNullTerminatedString(bytereader_t reader, uint8_t encoding, ParsedString &parsed)
{
switch(encoding)
{
case 0: // ISO-8859-1
if (bytereader_size(reader) == 0)
return NErr_Insufficient;
parsed.encoding = 0;
parsed.data = bytereader_pointer(reader);
parsed.byte_length = 0;
while (bytereader_size(reader) && bytereader_read_u8(reader))
parsed.byte_length++;
return NErr_Success;
case 1: // UTF-16
if (bytereader_size(reader) < 2)
return NErr_Insufficient;
parsed.byte_length = 0;
ParseBOM(reader, &parsed.encoding, 1);
parsed.data = bytereader_pointer(reader);
while (bytereader_size(reader) && bytereader_read_u16_le(reader))
parsed.byte_length+=2;
return NErr_Success;
case 2: // UTF-16BE
if (bytereader_size(reader) < 2)
return NErr_Insufficient;
parsed.byte_length = 0;
ParseBOM(reader, &parsed.encoding, 2);
parsed.data = bytereader_pointer(reader);
while (bytereader_size(reader) && bytereader_read_u16_le(reader))
parsed.byte_length+=2;
return NErr_Success;
case 3: // UTF-8
if (bytereader_size(reader) == 0)
return NErr_Insufficient;
parsed.encoding = 3;
parsed.data = bytereader_pointer(reader);
parsed.byte_length = 0;
size_t start = bytereader_size(reader);
#if 0 // TODO
/* check for UTF-8 BOM and skip it */
if (bytereader_size(reader) > 3 && bytereader_read_u8(reader) == 0xEF && bytereader_read_u8(reader) == 0xBB && bytereader_read_u8(reader) == 0xBF)
{
parsed.data = bytereader_pointer(reader);
parsed.byte_length = bytereader_size(reader);
}
else
{
/* no BOM but skip however far we read into the string */
size_t offset = start - bytereader_size(reader);
parsed.data = (const uint8_t *)parsed.data + offset;
parsed.byte_length -= offset;
}
#endif
/* finish it up */
while (bytereader_size(reader) && bytereader_read_u8(reader))
parsed.byte_length++;
return NErr_Success;
}
return NErr_Unknown;
}
int ParseFrameTerminatedString(bytereader_t reader, uint8_t encoding, ParsedString &parsed)
{
switch(encoding)
{
case 0: // ISO-8859-1
parsed.encoding = 0;
parsed.data = bytereader_pointer(reader);
parsed.byte_length = bytereader_size(reader);
return NErr_Success;
case 1: // UTF-16
if ((bytereader_size(reader) & 1) == 1)
return NErr_Error;
ParseBOM(reader, &parsed.encoding, 1);
parsed.data = bytereader_pointer(reader);
parsed.byte_length = bytereader_size(reader);
return NErr_Success;
case 2: // UTF-16BE
if ((bytereader_size(reader) & 1) == 1)
return NErr_Error;
ParseBOM(reader, &parsed.encoding, 2);
parsed.data = bytereader_pointer(reader);
parsed.byte_length = bytereader_size(reader);
return NErr_Success;
case 3: // UTF-8
parsed.encoding = 3;
parsed.data = bytereader_pointer(reader);
parsed.byte_length = bytereader_size(reader);
if (bytereader_size(reader) > 3 && bytereader_read_u8(reader) == 0xEF && bytereader_read_u8(reader) == 0xBB && bytereader_read_u8(reader) == 0xBF)
{
parsed.data = bytereader_pointer(reader);
parsed.byte_length = bytereader_size(reader);
}
return NErr_Success;
}
return NErr_Error;
}
int NXStringCreateFromParsedString(nx_string_t *value, ParsedString &parsed, int text_flags)
{
switch(parsed.encoding)
{
case 0: // ISO-8859-1
if (parsed.byte_length == 0)
return NXStringCreateEmpty(value);
if (text_flags & NSID3V2_TEXT_SYSTEM)
return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_system);
else
return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_latin1);
case 1: // UTF-16
if (parsed.byte_length < 2)
return NXStringCreateEmpty(value);
return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_utf16le);
case 2: // UTF-16BE
if (parsed.byte_length < 2)
return NXStringCreateEmpty(value);
return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_utf16be);
case 3: // UTF-8
if (parsed.byte_length == 0)
return NXStringCreateEmpty(value);
return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_utf8);
default:
return NErr_Unknown;
}
}
bool DescriptionMatches(const ParsedString &parsed, const char *description, int text_flags)
{
// see if our description matches
switch(parsed.encoding)
{
case 0: // ISO-8859-1
return !strcasecmp(description, (const char *)parsed.data);
case 1:
{
bytereader_value_t utf16;
bytereader_init(&utf16, parsed.data, parsed.byte_length);
while (*description && bytereader_size(&utf16))
{
if ((*description++ & ~0x20) != (bytereader_read_u16_le(&utf16) & ~0x20))
return false;
}
if (*description == 0 && bytereader_size(&utf16) == 0)
return true;
else
return false;
}
case 2:
{
bytereader_value_t utf16;
bytereader_init(&utf16, parsed.data, parsed.byte_length);
while (*description && bytereader_size(&utf16))
{
if ((*description++ & ~0x20) != (bytereader_read_u16_be(&utf16) & ~0x20))
return false;
}
if (*description == 0 && bytereader_size(&utf16) == 0)
return true;
else
return false;
}
case 3:
return !strcasecmp(description, (const char *)parsed.data);
}
return false;
}