586 lines
14 KiB
C++
586 lines
14 KiB
C++
/*
|
|
** Copyright © 2003-2014 Winamp SA
|
|
**
|
|
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
|
** liable for any damages arising from the use of this software.
|
|
**
|
|
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
|
** alter it and redistribute it freely, subject to the following restrictions:
|
|
**
|
|
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
|
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
|
**
|
|
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
|
**
|
|
** 3. This notice may not be removed or altered from any source distribution.
|
|
**
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include "ml.h"
|
|
|
|
void freeRecord(itemRecord *p)
|
|
{
|
|
if (p)
|
|
{
|
|
free(p->title);
|
|
free( p->artist );
|
|
free( p->ext );
|
|
free(p->comment);
|
|
free(p->album);
|
|
free(p->genre);
|
|
free(p->filename);
|
|
if (p->extended_info)
|
|
{
|
|
for (int x = 0; p->extended_info[x]; x ++)
|
|
free(p->extended_info[x]);
|
|
free(p->extended_info);
|
|
p->extended_info = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void freeRecordList(itemRecordList *obj)
|
|
{
|
|
if (obj)
|
|
{
|
|
emptyRecordList(obj);
|
|
free(obj->Items);
|
|
obj->Items=0;
|
|
obj->Alloc=obj->Size=0;
|
|
}
|
|
}
|
|
|
|
void emptyRecordList(itemRecordList *obj)
|
|
{
|
|
if (obj)
|
|
{
|
|
itemRecord *p=obj->Items;
|
|
while (obj->Size-->0)
|
|
{
|
|
freeRecord(p);
|
|
p++;
|
|
}
|
|
obj->Size=0;
|
|
}
|
|
}
|
|
|
|
void allocRecordList(itemRecordList *obj, int newsize, int granularity)
|
|
{
|
|
if (newsize < obj->Alloc || newsize < obj->Size) return;
|
|
|
|
int old_Alloc = obj->Alloc;
|
|
obj->Alloc=newsize+granularity;
|
|
|
|
itemRecord *data = (itemRecord*)realloc(obj->Items, sizeof(itemRecord) * obj->Alloc);
|
|
if (!data)
|
|
{
|
|
data = (itemRecord*)malloc(sizeof(itemRecord) * obj->Alloc);
|
|
if (data)
|
|
{
|
|
memcpy(data, obj->Items, sizeof(itemRecord) * old_Alloc);
|
|
free(obj->Items);
|
|
obj->Items = data;
|
|
}
|
|
else
|
|
obj->Alloc = old_Alloc;
|
|
}
|
|
else
|
|
obj->Items = data;
|
|
|
|
if (!old_Alloc) memset(data, 0, sizeof(itemRecordW) * obj->Alloc);
|
|
if (!obj->Items) obj->Alloc = 0;
|
|
}
|
|
|
|
void compactRecordList(itemRecordListW *obj)
|
|
{
|
|
if (obj->Size && obj->Size < obj->Alloc - 1024)
|
|
{
|
|
size_t old_Alloc = obj->Size;
|
|
obj->Alloc = obj->Size;
|
|
|
|
itemRecordW *data = (itemRecordW *)realloc(obj->Items, sizeof(itemRecordW) * obj->Alloc);
|
|
if (!data)
|
|
{
|
|
data = (itemRecordW *)malloc(sizeof(itemRecordW) * obj->Alloc);
|
|
if (data)
|
|
{
|
|
memcpy(data, obj->Items, sizeof(itemRecordW) * old_Alloc);
|
|
free(obj->Items);
|
|
obj->Items = data;
|
|
}
|
|
else
|
|
obj->Alloc = (int)old_Alloc;
|
|
}
|
|
else
|
|
obj->Items = data;
|
|
}
|
|
}
|
|
|
|
void copyRecord(itemRecord *out, const itemRecord *in)
|
|
{
|
|
#define COPYSTR(FOO) out->FOO = in->FOO ? _strdup(in->FOO) : 0;
|
|
COPYSTR(filename)
|
|
COPYSTR( title )
|
|
COPYSTR( ext )
|
|
COPYSTR(album)
|
|
COPYSTR(artist)
|
|
COPYSTR(comment)
|
|
COPYSTR(genre)
|
|
out->year=in->year;
|
|
out->track=in->track;
|
|
out->length=in->length;
|
|
#undef COPYSTR
|
|
out->extended_info=0;
|
|
|
|
if (in->extended_info)
|
|
{
|
|
for (int y = 0; in->extended_info[y]; y ++)
|
|
{
|
|
char *p=in->extended_info[y];
|
|
if (*p) setRecordExtendedItem(out,p,p+strlen(p)+1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void copyRecordList(itemRecordList *out, const itemRecordList *in)
|
|
{
|
|
allocRecordList(out,out->Size+in->Size,0);
|
|
if (!out->Items) return;
|
|
for (int x = 0; x < in->Size; x ++)
|
|
{
|
|
copyRecord(&out->Items[out->Size++],&in->Items[x]);
|
|
}
|
|
}
|
|
|
|
char *getRecordExtendedItem(const itemRecord *item, const char *name)
|
|
{
|
|
if (item->extended_info)
|
|
{
|
|
for (int x = 0; item->extended_info[x]; x ++)
|
|
{
|
|
if (!_stricmp(item->extended_info[x],name))
|
|
return item->extended_info[x]+strlen(name)+1;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void setRecordExtendedItem(itemRecord *item, const char *name, char *value)
|
|
{
|
|
int x=0;
|
|
if (item->extended_info)
|
|
{
|
|
for (x = 0; item->extended_info[x]; x ++)
|
|
{
|
|
if (!_stricmp(item->extended_info[x],name))
|
|
{
|
|
size_t name_len = strlen(name), value_len = strlen(value);
|
|
if (value_len>strlen(item->extended_info[x]+name_len+1))
|
|
{
|
|
free(item->extended_info[x]);
|
|
item->extended_info[x]=(char*)malloc(name_len+value_len+2);
|
|
}
|
|
strncpy(item->extended_info[x], name, name_len);
|
|
strncpy(item->extended_info[x]+strlen(name)+1, value, value_len);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// x=number of valid items.
|
|
char **data = (char**)realloc(item->extended_info,sizeof(char*) * (x+2));
|
|
if (data)
|
|
{
|
|
// if we could allocate then add, otherwise we'll have to skip
|
|
item->extended_info = data;
|
|
|
|
size_t name_len = strlen(name), value_len = strlen(value);
|
|
item->extended_info[x]=(char*)malloc(name_len+value_len+2);
|
|
strncpy(item->extended_info[x], name, name_len);
|
|
strncpy(item->extended_info[x]+name_len+1, value, value_len);
|
|
|
|
item->extended_info[x+1]=0;
|
|
}
|
|
else
|
|
{
|
|
data=(char**)_aligned_malloc(sizeof(char*) * (x+2), 16);
|
|
if (data)
|
|
{
|
|
memcpy(data, item->extended_info, sizeof(char*) * x);
|
|
free(item->extended_info);
|
|
|
|
// if we could allocate then add, otherwise we'll have to skip
|
|
item->extended_info = data;
|
|
|
|
size_t name_len = strlen(name), value_len = strlen(value);
|
|
item->extended_info[x]=(char*)malloc(name_len+value_len+2);
|
|
strncpy(item->extended_info[x], name, name_len);
|
|
strncpy(item->extended_info[x]+name_len+1, value, value_len);
|
|
|
|
item->extended_info[x+1]=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
----------------------------------
|
|
wide version starts here
|
|
----------------------------------
|
|
*/
|
|
void freeRecord(itemRecordW *p)
|
|
{
|
|
if (p)
|
|
{
|
|
free(p->title);
|
|
free(p->artist);
|
|
free(p->comment);
|
|
free(p->album);
|
|
free(p->genre);
|
|
free(p->filename);
|
|
free(p->albumartist);
|
|
free(p->replaygain_album_gain);
|
|
free(p->replaygain_track_gain);
|
|
free(p->publisher);
|
|
free(p->composer);
|
|
if (p->extended_info)
|
|
{
|
|
for (int x = 0; p->extended_info[x].key; x ++)
|
|
{
|
|
free(p->extended_info[x].key);
|
|
free(p->extended_info[x].value);
|
|
}
|
|
free(p->extended_info);
|
|
p->extended_info = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void freeRecordList(itemRecordListW *obj)
|
|
{
|
|
if (obj)
|
|
{
|
|
emptyRecordList(obj);
|
|
free(obj->Items);
|
|
obj->Items=0;
|
|
obj->Alloc=obj->Size=0;
|
|
}
|
|
}
|
|
|
|
void emptyRecordList(itemRecordListW *obj)
|
|
{
|
|
if (obj)
|
|
{
|
|
itemRecordW *p=obj->Items;
|
|
while (obj->Size-->0)
|
|
{
|
|
freeRecord(p);
|
|
p++;
|
|
}
|
|
obj->Size=0;
|
|
}
|
|
}
|
|
|
|
void allocRecordList(itemRecordListW *obj, int newsize, int granularity)
|
|
{
|
|
if (newsize < obj->Alloc || newsize < obj->Size) return;
|
|
|
|
int old_Alloc = obj->Alloc;
|
|
obj->Alloc=newsize+granularity;
|
|
itemRecordW *data = (itemRecordW*)realloc(obj->Items,sizeof(itemRecordW)*obj->Alloc);
|
|
if (!data)
|
|
{
|
|
data = (itemRecordW*)malloc(sizeof(itemRecordW) * obj->Alloc);
|
|
if (data)
|
|
{
|
|
memcpy(data, obj->Items, sizeof(itemRecordW) * obj->Alloc);
|
|
free(obj->Items);
|
|
obj->Items = data;
|
|
}
|
|
else
|
|
obj->Alloc = old_Alloc;
|
|
}
|
|
else
|
|
obj->Items = data;
|
|
|
|
if (!old_Alloc) memset(data, 0, sizeof(itemRecordW) * obj->Alloc);
|
|
if (!obj->Items) obj->Alloc = 0;
|
|
}
|
|
|
|
void copyRecord(itemRecordW *out, const itemRecordW *in)
|
|
{
|
|
#define COPYSTR(FOO) out->FOO = in->FOO ? _wcsdup(in->FOO) : 0;
|
|
#define COPY(FOO) out->FOO = in->FOO;
|
|
COPYSTR(filename);
|
|
COPYSTR(title);
|
|
COPYSTR(album);
|
|
COPYSTR(artist);
|
|
COPYSTR(comment);
|
|
COPYSTR(genre);
|
|
COPYSTR(albumartist);
|
|
COPYSTR(replaygain_album_gain);
|
|
COPYSTR(replaygain_track_gain);
|
|
COPYSTR(publisher);
|
|
COPYSTR(composer);
|
|
COPY(year);
|
|
COPY(track);
|
|
COPY(tracks);
|
|
COPY(length);
|
|
COPY(rating);
|
|
COPY(playcount);
|
|
COPY(lastplay);
|
|
COPY(lastupd);
|
|
COPY(filetime);
|
|
COPY(filesize);
|
|
COPY(bitrate);
|
|
COPY(type);
|
|
COPY(disc);
|
|
COPY(discs);
|
|
COPY(bpm);
|
|
COPYSTR(category);
|
|
#undef COPYSTR
|
|
out->extended_info=0;
|
|
|
|
if (in->extended_info)
|
|
{
|
|
for (int y = 0; in->extended_info[y].key; y ++)
|
|
{
|
|
setRecordExtendedItem(out,in->extended_info[y].key,in->extended_info[y].value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void copyRecordList(itemRecordListW *out, const itemRecordListW *in)
|
|
{
|
|
allocRecordList(out,out->Size+in->Size,0);
|
|
if (!out->Items) return;
|
|
for (int x = 0; x < in->Size; x ++)
|
|
{
|
|
copyRecord(&out->Items[out->Size++],&in->Items[x]);
|
|
}
|
|
}
|
|
|
|
wchar_t *getRecordExtendedItem(const itemRecordW *item, const wchar_t *name)
|
|
{
|
|
if (item->extended_info)
|
|
{
|
|
for (size_t x = 0; item->extended_info[x].key; x ++)
|
|
{
|
|
if (!_wcsicmp(item->extended_info[x].key,name))
|
|
return item->extended_info[x].value;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
wchar_t *getRecordExtendedItem_fast(const itemRecordW *item, const wchar_t *name)
|
|
{
|
|
if (item->extended_info)
|
|
{
|
|
for (size_t x = 0; item->extended_info[x].key; x ++)
|
|
{
|
|
if (item->extended_info[x].key == name)
|
|
return item->extended_info[x].value;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void setRecordExtendedItem(itemRecordW *item, const wchar_t *name, const wchar_t *value)
|
|
{
|
|
size_t x=0;
|
|
if (item->extended_info) for (x = 0; item->extended_info[x].key; x ++)
|
|
{
|
|
if (!_wcsicmp(item->extended_info[x].key,name))
|
|
{
|
|
free(item->extended_info[x].value);
|
|
item->extended_info[x].value = _wcsdup(value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// x=number of valid items.
|
|
extendedInfoW *data = (extendedInfoW *)realloc(item->extended_info, sizeof(extendedInfoW) * (x+2));
|
|
if (data)
|
|
{
|
|
item->extended_info = data;
|
|
|
|
item->extended_info[x].key = _wcsdup(name);
|
|
item->extended_info[x].value = _wcsdup(value);
|
|
|
|
item->extended_info[x+1].key=0;
|
|
item->extended_info[x+1].value=0;
|
|
}
|
|
else
|
|
{
|
|
data=(extendedInfoW *)_aligned_malloc(sizeof(extendedInfoW) * (x+2), 16);
|
|
if (data)
|
|
{
|
|
item->extended_info=data;
|
|
|
|
item->extended_info[x].key = _wcsdup(name);
|
|
item->extended_info[x].value = _wcsdup(value);
|
|
|
|
item->extended_info[x+1].key=0;
|
|
item->extended_info[x+1].value=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// this version assumes that the 'name' won't already be in the itemRecord
|
|
void setRecordExtendedItem_fast(itemRecordW *item, const wchar_t *name, const wchar_t *value)
|
|
{
|
|
int x=0;
|
|
if (item->extended_info)
|
|
{
|
|
for (x = 0; item->extended_info[x].key; x ++)
|
|
{
|
|
}
|
|
}
|
|
|
|
// x=number of valid items.
|
|
extendedInfoW *data=(extendedInfoW *)_aligned_realloc(item->extended_info,sizeof(extendedInfoW) * (x+2), 16);
|
|
if (data)
|
|
{
|
|
item->extended_info=data;
|
|
|
|
item->extended_info[x].key = _wcsdup(name);
|
|
item->extended_info[x].value = _wcsdup(value);
|
|
|
|
item->extended_info[x+1].key=0;
|
|
item->extended_info[x+1].value=0;
|
|
}
|
|
else
|
|
{
|
|
data=(extendedInfoW *)_aligned_malloc(sizeof(extendedInfoW) * (x+2), 16);
|
|
if (data)
|
|
{
|
|
item->extended_info=data;
|
|
|
|
item->extended_info[x].key = _wcsdup(name);
|
|
item->extended_info[x].value = _wcsdup(value);
|
|
|
|
item->extended_info[x+1].key=0;
|
|
item->extended_info[x+1].value=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: redo this without AutoChar
|
|
#include "../nu/AutoChar.h"
|
|
#define COPY_EXTENDED_STR(field) if (input-> ## field && input-> ## field ## [0]) setRecordExtendedItem(output, #field, AutoChar(input-> ## field));
|
|
#define COPY_EXTENDED_INT(field) if (input->##field > 0) { char temp[64] = {0}; _itoa(input->##field, temp, 10); setRecordExtendedItem(output, #field, temp); }
|
|
#define COPY_EXTENDED_INT64(field) if (input->##field > 0) { char temp[64] = {0}; _i64toa(input->##field, temp, 10); setRecordExtendedItem(output, #field, temp); }
|
|
#define COPY_EXTENDED_INT0(field) if (input->##field >= 0) { char temp[64] = {0}; _itoa(input->##field, temp, 10); setRecordExtendedItem(output, #field, temp); }
|
|
void convertRecord(itemRecord *output, const itemRecordW *input)
|
|
{
|
|
output->filename=AutoCharDup(input->filename);
|
|
output->title = AutoCharDup( input->title );
|
|
output->ext = AutoCharDup( input->ext );
|
|
output->album=AutoCharDup(input->album);
|
|
output->artist=AutoCharDup(input->artist);
|
|
output->comment=AutoCharDup(input->comment);
|
|
output->genre=AutoCharDup(input->genre);
|
|
output->year=input->year;
|
|
output->track=input->track;
|
|
output->length=input->length;
|
|
output->extended_info=0;
|
|
COPY_EXTENDED_STR(albumartist);
|
|
COPY_EXTENDED_STR(replaygain_album_gain);
|
|
COPY_EXTENDED_STR(replaygain_track_gain);
|
|
COPY_EXTENDED_STR(publisher);
|
|
COPY_EXTENDED_STR(composer);
|
|
COPY_EXTENDED_INT(tracks);
|
|
COPY_EXTENDED_INT(rating);
|
|
COPY_EXTENDED_INT(playcount);
|
|
COPY_EXTENDED_INT64(lastplay);
|
|
COPY_EXTENDED_INT64(lastupd);
|
|
COPY_EXTENDED_INT64(filetime);
|
|
COPY_EXTENDED_INT(filesize);
|
|
COPY_EXTENDED_INT(bitrate);
|
|
COPY_EXTENDED_INT0(type);
|
|
COPY_EXTENDED_INT(disc);
|
|
COPY_EXTENDED_INT(discs);
|
|
COPY_EXTENDED_INT(bpm);
|
|
COPY_EXTENDED_STR(category);
|
|
|
|
if (input->extended_info)
|
|
{
|
|
for (int y = 0; input->extended_info[y].key; y ++)
|
|
{
|
|
setRecordExtendedItem(output, AutoChar(input->extended_info[y].key), AutoChar(input->extended_info[y].value));
|
|
}
|
|
}
|
|
}
|
|
#undef COPY_EXTENDED_STR
|
|
#undef COPY_EXTENDED_INT
|
|
#undef COPY_EXTENDED_INT0
|
|
|
|
#include "../nu/AutoWide.h"
|
|
#define COPY_EXTENDED_STR(field) output->##field = AutoWideDup(getRecordExtendedItem(input, #field));
|
|
#define COPY_EXTENDED_INT(field) { char *x = getRecordExtendedItem(input, #field); output->##field=x?atoi(x):-1; }
|
|
|
|
void convertRecord(itemRecordW *output, const itemRecord *input)
|
|
{
|
|
output->filename=AutoWideDup(input->filename);
|
|
output->title=AutoWideDup(input->title);
|
|
output->ext = AutoWideDup( input->ext );
|
|
output->album=AutoWideDup(input->album);
|
|
output->artist=AutoWideDup(input->artist);
|
|
output->comment=AutoWideDup(input->comment);
|
|
output->genre=AutoWideDup(input->genre);
|
|
output->year=input->year;
|
|
output->track=input->track;
|
|
output->length=input->length;
|
|
output->extended_info=0;
|
|
COPY_EXTENDED_STR(albumartist);
|
|
COPY_EXTENDED_STR(replaygain_album_gain);
|
|
COPY_EXTENDED_STR(replaygain_track_gain);
|
|
COPY_EXTENDED_STR(publisher);
|
|
COPY_EXTENDED_STR(composer);
|
|
COPY_EXTENDED_INT(tracks);
|
|
COPY_EXTENDED_INT(rating);
|
|
COPY_EXTENDED_INT(playcount);
|
|
COPY_EXTENDED_INT(lastplay);
|
|
COPY_EXTENDED_INT(lastupd);
|
|
COPY_EXTENDED_INT(filetime);
|
|
COPY_EXTENDED_INT(filesize);
|
|
COPY_EXTENDED_INT(type);
|
|
COPY_EXTENDED_INT(disc);
|
|
COPY_EXTENDED_INT(discs);
|
|
COPY_EXTENDED_INT(bpm);
|
|
COPY_EXTENDED_INT(bitrate);
|
|
COPY_EXTENDED_STR(composer);
|
|
COPY_EXTENDED_STR(category);
|
|
// TODO: copy input's extended fields
|
|
}
|
|
#undef COPY_EXTENDED_STR
|
|
#undef COPY_EXTENDED_INT
|
|
|
|
void convertRecordList(itemRecordList *output, const itemRecordListW *input)
|
|
{
|
|
output->Alloc = output->Size = input->Size;
|
|
output->Items = (itemRecord*)calloc(sizeof(itemRecord),input->Size);
|
|
if (output->Items)
|
|
{
|
|
for(int i=0; i < input->Size; i++)
|
|
{
|
|
convertRecord(&output->Items[i],&input->Items[i]);
|
|
}
|
|
}
|
|
else
|
|
output->Alloc = output->Size = 0;
|
|
}
|
|
|
|
void convertRecordList(itemRecordListW *output, const itemRecordList *input)
|
|
{
|
|
output->Alloc = output->Size = input->Size;
|
|
output->Items = (itemRecordW*)calloc(sizeof(itemRecordW),input->Size);
|
|
if (output->Items)
|
|
{
|
|
for(int i=0; i < input->Size; i++)
|
|
{
|
|
convertRecord(&output->Items[i],&input->Items[i]);
|
|
}
|
|
}
|
|
} |