//----------------------------------------------------------------------------- // // Musepack Demuxer // // Author : Igor Janos // //----------------------------------------------------------------------------- #include "stdafx.h" //----------------------------------------------------------------------------- // // APE_Tag_Item class // //----------------------------------------------------------------------------- APE_Tag_Item::APE_Tag_Item() { name = _T(""); value = _T(""); type = APE_Tag_Item::TYPE_STRING; buf = NULL; len = 0; } APE_Tag_Item::~APE_Tag_Item() { if (buf) { free(buf); buf = NULL; } } int APE_Tag_Item::Load(BYTE *buf, BYTE *end) { // there must be at least 8 bytes to read the size and flags int left = end - buf; if (left < 8) return -1; BYTE *orig = buf; // remember the starting position DWORD size = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; DWORD flags = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; // key is ASCII int keylen = 0; char *start = (char*)buf; // make sure we access only those bytes we can while (buf < end) { if (buf[0] == 0) { break; } else { keylen ++; buf++; } } CStringA akey; char *dst = akey.GetBufferSetLength(keylen); if (!dst) return -1; // not enough memory memcpy(dst, start, keylen); name = akey; // skip the '0' terminator buf ++; // and make sure there is enough bytes for us to read if (buf + size > end) return -1; int t = (flags >> 1) & 0x03; // 2..1 bits switch (t) { case 0: type = APE_Tag_Item::TYPE_STRING; break; case 1: type = APE_Tag_Item::TYPE_BINARY; break; case 2: type = APE_Tag_Item::TYPE_LOCATOR; break; default: { // reserved return -1; } break; } if (type == APE_Tag_Item::TYPE_BINARY) { // read the binary buffer this->buf = (BYTE*)malloc(size); if (!this->buf) return -1; // out of memory // read the bytes memcpy(this->buf, buf, size); len = size; buf += size; } else { // read the UTF-8 string int cnt = size*2; WCHAR *wstr = (WCHAR*)malloc(cnt * sizeof(WCHAR)); if (!wstr) return -1; memset(wstr, 0, cnt*sizeof(WCHAR)); // convert the string MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, size, wstr, cnt); // and save the value value = CString(wstr); free(wstr); buf += size; } // returns the number of bytes eaten return (buf - orig); } //----------------------------------------------------------------------------- // // APE_Tag class // //----------------------------------------------------------------------------- APE_Tag::APE_Tag() { } APE_Tag::~APE_Tag() { Clear(); } void APE_Tag::Clear() { for (int i=0; itype == APE_Tag_Item::TYPE_BINARY) return _T(""); return tag->value; } APE_Tag_Item *APE_Tag::Find(CString name) { CString name_lc = name; name_lc.MakeLower(); for (int i=0; iname; in.MakeLower(); // compare strings in lower case if (in == name_lc) { return item; } } return NULL; } int APE_Tag::ReadFooter(BYTE *buf, int len, int *tagsize) { /* Footer must be exactly 32 bytes long. */ if (len < 32) return -1; // check the preamble BYTE preamble[8] = { 'A','P','E','T','A','G','E','X' }; BYTE reserved[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; if (memcmp(buf, preamble, 8) != 0) return -1; buf += 8; DWORD ver = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; DWORD size = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; DWORD cnt = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; DWORD flag = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; if (ver != 2000) return -1; // not an APE Tag v2.0 // check the flags bool hdr = (flag >> 31) & 0x01; if (!hdr) return -1; int ft = (flag >> 29) & 0x01; if (ft) return -1; // not a footer // 8 zero bytes if (memcmp(buf, reserved, 8) != 0) return -1; // return the size of APE Tag and finish if (tagsize) *tagsize = (int)size; return 0; } int APE_Tag::ReadTag(BYTE *buf, int len, bool skip_preamble) { int req_len = 32; if (skip_preamble) req_len -= 8; if (len < req_len) return -1; // end of our buffer BYTE *end = buf + len; BYTE preamble[8] = { 'A','P','E','T','A','G','E','X' }; BYTE reserved[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; // check the preamble if (!skip_preamble) { if (memcmp(buf, preamble, 8) != 0) return -1; buf += 8; len -= 8; } DWORD ver = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; DWORD size = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; DWORD cnt = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; DWORD flag = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]); buf += 4; len -= 16; if (ver != 2000) return -1; // not an APE Tag v2.0 // check the flags bool hdr = (flag >> 31) & 0x01; if (!hdr) return -1; int hd = (flag >> 29) & 0x01; if (!hd) return -1; // not a header // 8 zero bytes if (memcmp(buf, reserved, 8) != 0) return -1; // start of the items buf += 8; len -= 8; // do we have enough data ? if (len < size) return -1; // now read all the tag items for (int i=0; iLoad(buf, end); if (ret <= 0) { // cannot load item - abort loading delete item; break; } else { // keep the item items.push_back(item); // and continue buf += ret; } } return 0; }