00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <tstring.h>
00023 #include <wmafile.h>
00024 #include <wmatag.h>
00025 #include <wmaproperties.h>
00026
00027 using namespace TagLib;
00028
00029 class WMA::File::FilePrivate
00030 {
00031 public:
00032 FilePrivate(): size(0), offset1(0), offset2(0), size1(0), size2(0),
00033 numObjects(0), tag(0), properties(0) {}
00034 unsigned long long size;
00035 unsigned long offset1, offset2, size1, size2, numObjects;
00036 WMA::Tag *tag;
00037 WMA::Properties *properties;
00038 };
00039
00040
00041
00042 struct WMA::GUID
00043 {
00044 WMA::DWORD v1;
00045 WMA::WORD v2;
00046 WMA::WORD v3;
00047 WMA::BYTE v4[8];
00048 bool operator==(const GUID &g) const { return memcmp(this, &g, sizeof(WMA::GUID)) == 0; }
00049 bool operator!=(const GUID &g) const { return memcmp(this, &g, sizeof(WMA::GUID)) != 0; }
00050 static GUID header;
00051 static GUID fileProperties;
00052 static GUID streamProperties;
00053 static GUID contentDescription;
00054 static GUID extendedContentDescription;
00055 static GUID audioMedia;
00056 };
00057
00058 WMA::GUID WMA::GUID::header = {
00059 0x75B22630, 0x668E, 0x11CF, { 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C }
00060 };
00061
00062 WMA::GUID WMA::GUID::fileProperties = {
00063 0x8CABDCA1, 0xA947, 0x11CF, { 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 },
00064 };
00065
00066 WMA::GUID WMA::GUID::streamProperties = {
00067 0xB7DC0791, 0xA9B7, 0x11CF, { 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 },
00068 };
00069
00070 WMA::GUID WMA::GUID::contentDescription = {
00071 0x75b22633, 0x668e, 0x11cf, { 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c },
00072 };
00073
00074 WMA::GUID WMA::GUID::extendedContentDescription = {
00075 0xD2D0A440, 0xE307, 0x11D2, { 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 },
00076 };
00077
00078 WMA::GUID WMA::GUID::audioMedia = {
00079 0xF8699E40, 0x5B4D, 0x11CF, { 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B },
00080 };
00081
00083
00085
00086 WMA::File::File(const char *file, bool readProperties, Properties::ReadStyle propertiesStyle)
00087 : TagLib::File(file)
00088 {
00089 d = new FilePrivate;
00090 read(readProperties, propertiesStyle);
00091 }
00092
00093 WMA::File::~File()
00094 {
00095 if(d) {
00096 if (d->tag)
00097 delete d->tag;
00098 if (d->properties)
00099 delete d->properties;
00100 delete d;
00101 }
00102 }
00103
00104 TagLib::Tag *WMA::File::tag() const
00105 {
00106 return d->tag;
00107 }
00108
00109 WMA::Tag *WMA::File::WMATag() const
00110 {
00111 return d->tag;
00112 }
00113
00114 WMA::Properties *WMA::File::audioProperties() const
00115 {
00116 return d->properties;
00117 }
00118
00119 void WMA::File::read(bool readProperties, Properties::ReadStyle )
00120 {
00121 WMA::GUID guid;
00122
00123 readGUID(guid);
00124 if(guid != GUID::header) {
00125 return;
00126 }
00127
00128 int length = 0;
00129 int bitrate = 0;
00130 int sampleRate = 0;
00131 int channels = 0;
00132
00133 d->tag = new WMA::Tag();
00134 if(!d->tag)
00135 return;
00136
00137 d->size = readQWORD();
00138 d->numObjects = readDWORD();
00139 seek(2, Current);
00140
00141 for(int i = 0; i < (int)d->numObjects; i++) {
00142
00143 readGUID(guid);
00144 long objectSize = (long)readQWORD();
00145
00146 if(readProperties && guid == GUID::fileProperties) {
00147
00148 seek(16+8+8+8, Current);
00149 length = (int)(readQWORD() / 10000000L);
00150 seek(8+8+4+4+4+4, Current);
00151
00152 }
00153
00154 else if(readProperties && guid == GUID::streamProperties) {
00155
00156 long pos = tell();
00157
00158 readGUID(guid);
00159 if(guid != GUID::audioMedia) {
00160 return;
00161 }
00162
00163 seek(16+8+4+4+2+4+2, Current);
00164 channels = readWORD();
00165 sampleRate = readDWORD();
00166 bitrate = readDWORD() * 8 / 1000;
00167
00168 seek(pos + (long)objectSize - 24);
00169 }
00170
00171 else if(guid == GUID::extendedContentDescription) {
00172
00173 d->offset2 = tell() - 16 - 8;
00174 d->size2 = (long)objectSize;
00175
00176 int numDescriptors = readWORD();
00177
00178 for(int j = 0; j < numDescriptors; j++) {
00179 WMA::Attribute attr(*this);
00180 d->tag->setAttribute(attr.name().toCString(false), attr);
00181 }
00182
00183 }
00184
00185 else if(guid == GUID::contentDescription) {
00186
00187 d->offset1 = tell() - 16 - 8;
00188 d->size1 = (long)objectSize;
00189
00190 int titleLength = readWORD();
00191 int artistLength = readWORD();
00192 int copyrightLength = readWORD();
00193 int commentLength = readWORD();
00194 int ratingLength = readWORD();
00195
00196 String value;
00197
00198 readString(titleLength, value);
00199 d->tag->setTitle(value);
00200
00201 readString(artistLength, value);
00202 d->tag->setArtist(value);
00203
00204 readString(copyrightLength, value);
00205 d->tag->setCopyright(value);
00206
00207 readString(commentLength, value);
00208 d->tag->setComment(value);
00209
00210 readString(ratingLength, value);
00211 d->tag->setRating(value);
00212 }
00213
00214 else {
00215 seek((long)objectSize - 24, Current);
00216 }
00217
00218 }
00219
00220 if(readProperties) {
00221 d->properties = new WMA::Properties();
00222 if(d->properties)
00223 d->properties->set(length, bitrate, sampleRate, channels);
00224 }
00225
00226 }
00227
00228 bool WMA::File::save()
00229 {
00230 if(readOnly()) {
00231 return false;
00232 }
00233
00234 if(d->offset1 == 0) {
00235 d->offset1 = 16 + 8 + 4 + 2;
00236 d->numObjects++;
00237 }
00238
00239 if(d->offset2 == 0) {
00240 d->offset2 = 16 + 8 + 4 + 2;
00241 d->numObjects++;
00242 }
00243
00244 ByteVector chunk1 = renderContentDescription();
00245 ByteVector chunk2 = renderExtendedContentDescription();
00246
00247 if(d->offset1 > d->offset2) {
00248 insert(chunk1, d->offset1, d->size1);
00249 insert(chunk2, d->offset2, d->size2);
00250 }
00251 else {
00252 insert(chunk2, d->offset2, d->size2);
00253 insert(chunk1, d->offset1, d->size1);
00254 }
00255
00256 insert(ByteVector::fromLongLong(d->size +
00257 (int)(chunk1.size() - d->size1) +
00258 (int)(chunk2.size() - d->size2), false) +
00259 ByteVector::fromUInt(d->numObjects, false), 16, 8 + 4);
00260
00261 return true;
00262 }
00263
00265
00267
00268 int WMA::File::readBYTE()
00269 {
00270 ByteVector v = readBlock(1);
00271 return v[0];
00272 }
00273
00274 int WMA::File::readWORD()
00275 {
00276 ByteVector v = readBlock(2);
00277 return v.toShort(false);
00278 }
00279
00280 unsigned int WMA::File::readDWORD()
00281 {
00282 ByteVector v = readBlock(4);
00283 return v.toUInt(false);
00284 }
00285
00286 long long WMA::File::readQWORD()
00287 {
00288 ByteVector v = readBlock(8);
00289 return v.toLongLong(false);
00290 }
00291
00292 void WMA::File::readGUID(GUID &g)
00293 {
00294 g.v1 = readDWORD();
00295 g.v2 = readWORD();
00296 g.v3 = readWORD();
00297 for(int i = 0; i < 8; i++)
00298 g.v4[i] = readBYTE();
00299 }
00300
00301 void WMA::File::readString(int len, String &s)
00302 {
00303 ByteVector v = readBlock(len);
00304 if(len < 2 || v[len-1] != 0 || v[len-2] != 0)
00305 v.append(ByteVector::fromShort(0));
00306 s = String(v, String::UTF16LE);
00307 }
00308
00309 ByteVector WMA::File::renderContentDescription()
00310 {
00311 String s;
00312
00313 s = d->tag->title();
00314 ByteVector v1 = s.data(String::UTF16LE);
00315 if(s.size()) {
00316 v1.append((char)0);
00317 v1.append((char)0);
00318 }
00319
00320 s = d->tag->artist();
00321 ByteVector v2 = s.data(String::UTF16LE);
00322 if(s.size()) {
00323 v2.append((char)0);
00324 v2.append((char)0);
00325 }
00326
00327 s = d->tag->copyright();
00328 ByteVector v3 = s.data(String::UTF16LE);
00329 if(s.size()) {
00330 v3.append((char)0);
00331 v3.append((char)0);
00332 }
00333
00334 s = d->tag->comment();
00335 ByteVector v4 = s.data(String::UTF16LE);
00336 if(s.size()) {
00337 v4.append((char)0);
00338 v4.append((char)0);
00339 }
00340
00341 s = d->tag->rating();
00342 ByteVector v5 = s.data(String::UTF16LE);
00343 if(s.size()) {
00344 v5.append((char)0);
00345 v5.append((char)0);
00346 }
00347
00348 ByteVector data;
00349
00350 data.append(ByteVector::fromShort(v1.size(), false));
00351 data.append(ByteVector::fromShort(v2.size(), false));
00352 data.append(ByteVector::fromShort(v3.size(), false));
00353 data.append(ByteVector::fromShort(v4.size(), false));
00354 data.append(ByteVector::fromShort(v5.size(), false));
00355
00356 data.append(v1);
00357 data.append(v2);
00358 data.append(v3);
00359 data.append(v4);
00360 data.append(v5);
00361
00362 data = ByteVector(reinterpret_cast<const char *>(&GUID::contentDescription), sizeof(GUID))
00363 + ByteVector::fromLongLong(data.size() + 16 + 8, false)
00364 + data;
00365
00366 return data;
00367 }
00368
00369 ByteVector WMA::File::renderExtendedContentDescription()
00370 {
00371 ByteVector data;
00372
00373 data.append(ByteVector::fromShort(d->tag->attributeMap().size(), false));
00374
00375 WMA::AttributeMap::ConstIterator it = d->tag->attributeMap().begin();
00376 for(; it != d->tag->attributeMap().end(); it++)
00377 data.append(it->second.render());
00378
00379 data = ByteVector(reinterpret_cast<const char *>(&GUID::extendedContentDescription), sizeof(GUID))
00380 + ByteVector::fromLongLong(data.size() + 16 + 8, false)
00381 + data;
00382
00383 return data;
00384 }
00385
00386