/* * * * Copyright (c) 2007 Will Fisher (will.fisher@gmail.com) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * */ #include "iPodArtworkDB.h" #include #include //utilities #define SAFEDELETE(x) {if(x) delete (x); (x)=0;} #define SAFEFREE(x) {if(x) free(x); (x)=0;} static __forceinline unsigned short rev1(const BYTE *data) { return ((unsigned short) data[0]); } static __forceinline unsigned short rev1i(const BYTE *data, int &ptr) { unsigned short ret = rev1(data+ptr); ptr+=1; return ret; } static __forceinline unsigned short rev2(const BYTE *data) { unsigned short ret; ret = ((unsigned short) data[1]) << 8; ret += ((unsigned short) data[0]); return ret; } static __forceinline unsigned short rev2i(const BYTE *data, int &ptr) { unsigned short ret = rev2(data+ptr); ptr+=2; return ret; } // get 4 bytes from data, reversed static __forceinline unsigned int rev4(const BYTE * data) { unsigned int ret; ret = ((unsigned long) data[3]) << 24; ret += ((unsigned long) data[2]) << 16; ret += ((unsigned long) data[1]) << 8; ret += ((unsigned long) data[0]); return ret; } static __forceinline unsigned int rev4i(const BYTE * data, int &ptr) { unsigned int ret = rev4(data+ptr); ptr+=4; return ret; } // get 4 bytes from data static __forceinline unsigned long get4(const unsigned char * data) { unsigned long ret; ret = ((unsigned long) data[0]) << 24; ret += ((unsigned long) data[1]) << 16; ret += ((unsigned long) data[2]) << 8; ret += ((unsigned long) data[3]); return ret; } static __forceinline unsigned long get4i(const unsigned char * data, int &ptr) { unsigned long ret = get4(data+ptr); ptr+=4; return ret; } // get 8 bytes from data static __forceinline unsigned __int64 get8(const unsigned char * data) { unsigned __int64 ret; ret = get4(data); ret = ret << 32; ret+= get4(data+4); return ret; } // get 8 bytes from data static __forceinline unsigned __int64 get8i(const unsigned char * data, int &ptr) { unsigned __int64 ret = get8(data+ptr); ptr+=8; return ret; } // reverse 8 bytes in place static __forceinline unsigned __int64 rev8(unsigned __int64 number) { unsigned __int64 ret; ret = (number&0x00000000000000FF) << 56; ret+= (number&0x000000000000FF00) << 40; ret+= (number&0x0000000000FF0000) << 24; ret+= (number&0x00000000FF000000) << 8; ret+= (number&0x000000FF00000000) >> 8; ret+= (number&0x0000FF0000000000) >> 24; ret+= (number&0x00FF000000000000) >> 40; ret+= (number&0xFF00000000000000) >> 56; return ret; } static __forceinline void putmh(const char* x, BYTE *data, int &ptr) { data[0+ptr]=x[0]; data[1+ptr]=x[1]; data[2+ptr]=x[2]; data[3+ptr]=x[3]; ptr+=4; } //write 4 bytes reversed static __forceinline void rev4(const unsigned long number, unsigned char * data) { data[3] = (unsigned char)(number >> 24) & 0xff; data[2] = (unsigned char)(number >> 16) & 0xff; data[1] = (unsigned char)(number >> 8) & 0xff; data[0] = (unsigned char)number & 0xff; } static __forceinline void rev4i(const unsigned int number, BYTE* data, int &ptr) { rev4(number,data+ptr); ptr+=4; } static __forceinline void rev2(const unsigned short number, unsigned char * data) { data[1] = (unsigned char)(number >> 8) & 0xff; data[0] = (unsigned char)number & 0xff; } static __forceinline void rev2i(const unsigned short number, BYTE* data, int &ptr) { rev2(number,data+ptr); ptr+=2; } static __forceinline void rev1(const unsigned char number, unsigned char * data) { data[0] = number; } static __forceinline void rev1i(const unsigned char number, BYTE* data, int &ptr) { rev1(number,data+ptr); ptr+=1; } // write 8 bytes normal static __forceinline void put8(unsigned __int64 number, unsigned char * data) { data[0] = (unsigned char)(number >> 56) & 0xff; data[1] = (unsigned char)(number >> 48) & 0xff; data[2] = (unsigned char)(number >> 40) & 0xff; data[3] = (unsigned char)(number >> 32) & 0xff; data[4] = (unsigned char)(number >> 24) & 0xff; data[5] = (unsigned char)(number >> 16) & 0xff; data[6] = (unsigned char)(number >> 8) & 0xff; data[7] = (unsigned char)number & 0xff; } static __forceinline void put8i(unsigned __int64 number, unsigned char * data, int &ptr) { put8(number,data+ptr); ptr+=8; } static __forceinline void pad(BYTE * data, int endpoint, int& startpoint) { if(endpoint == startpoint) return; ZeroMemory(data+startpoint, endpoint - startpoint); startpoint = endpoint; } /////////////////////////////////////////////////////////////////////////////// // ArtDB ArtDB::ArtDB() : headerlen(0x84), totallen(0), unk1(0), unk2(2), unk3(0), nextid(0x40), unk5(0), unk6(0), unk7(0), unk8(0), unk9(0), unk10(0), unk11(0), imageListDS(0), albumListDS(0), fileListDS(0) { } ArtDB::~ArtDB() { SAFEDELETE(imageListDS); SAFEDELETE(albumListDS); SAFEDELETE(fileListDS); } int ArtDB::parse(BYTE * data, int len, wchar_t drive) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhfd",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x84) return -1; totallen = rev4i(data,ptr); unk1 = rev4i(data,ptr); unk2 = rev4i(data,ptr); int numchildren = rev4i(data,ptr); unk3 = rev4i(data,ptr); nextid = rev4i(data,ptr); unk5 = rev8(get8i(data,ptr)); unk6 = rev8(get8i(data,ptr)); unk7 = rev4i(data,ptr); unk8 = rev4i(data,ptr); unk9 = rev4i(data,ptr); unk10 = rev4i(data,ptr); unk11 = rev4i(data,ptr); ptr=headerlen; for(int i=0; iparse(data+ptr,len-ptr); if(p == -1) return -1; switch(d->index) { case 1: imageListDS = d; break; case 2: albumListDS = d; break; case 3: fileListDS = d; break; default: delete d; } ptr+=p; } if(!imageListDS) imageListDS = new ArtDataSet(1); if(!albumListDS) albumListDS = new ArtDataSet(2); if(!fileListDS) fileListDS = new ArtDataSet(3); for(ArtImageList::ArtImageMapIterator i = imageListDS->imageList->images.begin(); i!=imageListDS->imageList->images.end(); i++) { if(i->second) { for(auto j = i->second->dataobjs.begin(); j != i->second->dataobjs.end(); j++) { if((*j)->image) { ArtFile *f = fileListDS->fileList->getFile((*j)->image->corrid); if(!f) { f = new ArtFile(); f->corrid = (*j)->image->corrid; fileListDS->fileList->files.push_back(f); } f->images.push_back(new ArtFileImage((*j)->image->ithmboffset,(*j)->image->imagesize,1)); } } } } for(auto i = fileListDS->fileList->files.begin(); i!=fileListDS->fileList->files.end(); i++) { wchar_t file[MAX_PATH] = {0}; StringCchPrintfW(file, MAX_PATH, L"%c:\\iPod_Control\\Artwork\\F%04d_1.ithmb",drive,(*i)->corrid); (*i)->file = _wcsdup(file); (*i)->sortImages(); } return totallen; } int ArtDB::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhfd",data,ptr); rev4i(headerlen,data,ptr); rev4i(0,data,ptr); // fill total len here later rev4i(unk1,data,ptr); rev4i(unk2,data,ptr); // always seems to be "2" when iTunes writes it rev4i(3,data,ptr); // num children rev4i(unk3,data,ptr); rev4i(nextid,data,ptr); put8i(rev8(unk5),data,ptr); put8i(rev8(unk6),data,ptr); rev4i(unk7,data,ptr); rev4i(unk8,data,ptr); rev4i(unk9,data,ptr); rev4i(unk10,data,ptr); rev4i(unk11,data,ptr); pad(data,headerlen,ptr); // write out children int p; p = imageListDS->write(data+ptr,len-ptr); if(p<0) return -1; ptr+=p; p = albumListDS->write(data+ptr,len-ptr); if(p<0) return -1; ptr+=p; p = fileListDS->write(data+ptr,len-ptr); if(p<0) return -1; ptr+=p; rev4(ptr,&data[8]); // fill in total length return ptr; } void ArtDB::makeEmptyDB(wchar_t drive) { imageListDS = new ArtDataSet(1); albumListDS = new ArtDataSet(2); fileListDS = new ArtDataSet(3); for(auto i = fileListDS->fileList->files.begin(); i!=fileListDS->fileList->files.end(); i++) { wchar_t file[MAX_PATH] = {0}; StringCchPrintfW(file, MAX_PATH, L"%c:\\iPod_Control\\Artwork\\F%04d_1.ithmb",drive,(*i)->corrid); (*i)->file = _wcsdup(file); } } /////////////////////////////////////////////////////////////////////////////// // ArtDatSet ArtDataSet::ArtDataSet() : headerlen(0x60), totallen(0), index(0), imageList(0), albumList(0), fileList(0) { } ArtDataSet::ArtDataSet(int idx) : headerlen(0x60), totallen(0), index(idx), imageList(0), albumList(0), fileList(0) { switch(idx) { case 1: imageList = new ArtImageList; break; case 2: albumList = new ArtAlbumList; break; case 3: fileList = new ArtFileList; break; default: index=0; } } ArtDataSet::~ArtDataSet() { SAFEDELETE(imageList); SAFEDELETE(albumList); SAFEDELETE(fileList); } int ArtDataSet::parse(BYTE *data, int len) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhsd",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x60) return -1; totallen = rev4i(data,ptr); index = rev4i(data,ptr); ptr=headerlen; int p=0; switch(index) { case 1: imageList = new ArtImageList; p = imageList->parse(data+ptr, len-ptr); break; case 2: albumList = new ArtAlbumList; p = albumList->parse(data+ptr, len-ptr); break; case 3: fileList = new ArtFileList; p = fileList->parse(data+ptr, len-ptr); break; } if(p < 0) return -1; return totallen; } int ArtDataSet::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhsd",data,ptr); rev4i(headerlen,data,ptr); rev4i(0,data,ptr); // fill total len here later rev4i(index,data,ptr); pad(data,headerlen,ptr); int p=0; switch(index) { case 1: p=imageList->write(data+ptr, len-ptr); break; case 2: p=albumList->write(data+ptr, len-ptr); break; case 3: p=fileList->write(data+ptr, len-ptr); break; } if(p<0) return -1; ptr+=p; rev4(ptr,&data[8]); // fill in total length return ptr; } /////////////////////////////////////////////////////////////////////////////// // ArtImageList ArtImageList::ArtImageList() : headerlen(0x5c) { } ArtImageList::~ArtImageList() { for(ArtImageMapIterator f = images.begin(); f != images.end(); f++) delete f->second; images.clear(); } int ArtImageList::parse(BYTE *data, int len) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhli",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x5c) return -1; int children = rev4i(data,ptr); ptr=headerlen; for(int i=0; iparse(data+ptr,len-ptr); if(p<0) {delete f; return -1;} ptr+=p; images.insert(ArtImageMapPair(f->songid,f)); } return ptr; } int ArtImageList::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhli",data,ptr); rev4i(headerlen,data,ptr); rev4i(images.size(),data,ptr); pad(data,headerlen,ptr); for(ArtImageMapIterator f = images.begin(); f != images.end(); f++) { int p = f->second->write(data+ptr,len-ptr); if(p<0) return -1; ptr+=p; } return ptr; } /////////////////////////////////////////////////////////////////////////////// // ArtImage ArtImage::ArtImage() : headerlen(0x98), totallen(0), id(0), songid(0), unk4(0), rating(0), unk6(0), originalDate(0), digitizedDate(0), srcImageSize(0) { } ArtImage::~ArtImage() { for (auto obj : dataobjs) { delete obj; } dataobjs.clear(); } int ArtImage::parse(BYTE *data, int len) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhii",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x98) return -1; totallen = rev4i(data,ptr); int numchildren = rev4i(data,ptr); id = rev4i(data,ptr); songid = rev8(get8i(data,ptr)); unk4 = rev4i(data,ptr); rating = rev4i(data,ptr); unk6 = rev4i(data,ptr); originalDate = rev4i(data,ptr); digitizedDate = rev4i(data,ptr); srcImageSize = rev4i(data,ptr); ptr = headerlen; for(int i=0; iparse(data+ptr,len-ptr); if(p<0) { delete d; return -1; } ptr+=p; // fuck with d. ugh. if((d->type == 2 || d->type == 5) && d->data) { // this is a container mhod d->image = new ArtImageName; int p2 = d->image->parse(d->data,d->datalen); if(p2>0) { SAFEFREE(d->data); d->datalen=0; } else SAFEDELETE(d->image); } dataobjs.push_back(d); } return totallen; } template BYTE *expandMemWrite(T * x, int &len, int maxsize=1024000) { int s = 1024; for(;;) { BYTE *r = (BYTE*)malloc(s); int p = x->write(r,s); if(p>0) { len=p; return r; } free(r); s = s+s; if(s > maxsize) break; } return NULL; } int ArtImage::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhii",data,ptr); rev4i(headerlen,data,ptr); rev4i(0,data,ptr); // fill in total length later rev4i(dataobjs.size(),data,ptr); rev4i(id,data,ptr); put8i(rev8(songid),data,ptr); rev4i(unk4,data,ptr); rev4i(rating,data,ptr); rev4i(unk6,data,ptr); rev4i(originalDate,data,ptr); rev4i(digitizedDate,data,ptr); rev4i(srcImageSize,data,ptr); pad(data,headerlen,ptr); for(auto f = dataobjs.begin(); f != dataobjs.end(); f++) { if((*f)->image) { int len=0; BYTE *b = expandMemWrite((*f)->image,len); if(!b) return -1; (*f)->data = b; (*f)->datalen = len; } int p = (*f)->write(data+ptr,len-ptr); if((*f)->image) { SAFEFREE((*f)->data); (*f)->datalen=0; } if(p<0) return -1; ptr+=p; } rev4(ptr,&data[8]); // fill in total length return ptr; } /////////////////////////////////////////////////////////////////////////////// // ArtDataObj ArtDataObject::ArtDataObject() : headerlen(0x18), type(0), data(0), datalen(0), image(0), unk1(0) { } ArtDataObject::~ArtDataObject() { SAFEDELETE(image); SAFEFREE(data); } int ArtDataObject::parse(BYTE *data, int len) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhod",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x18) return -1; int totallen = rev4i(data,ptr); if(len < totallen) return -1; type = rev2i(data,ptr); unk1 = (unsigned char)rev1i(data,ptr); short padding = rev1i(data,ptr); ptr = headerlen; if(type == 3 && rev2(&data[totallen-2]) == 0) datalen = wcslen((wchar_t*)(data+ptr+12))*sizeof(wchar_t) + 12; else datalen = totallen - headerlen - padding; if(datalen > 0x400 || datalen < 0) return -1; this->data = (BYTE*)malloc(datalen); memcpy(this->data,data+ptr,datalen); return totallen; } int ArtDataObject::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhod",data,ptr); rev4i(headerlen,data,ptr); short padding = (4 - ((headerlen + datalen) % 4));// % 4; if(padding == 4) padding = 0; rev4i(headerlen+datalen+padding,data,ptr); rev2i(type,data,ptr); rev1i(unk1,data,ptr); rev1i((unsigned char)padding,data,ptr); pad(data,headerlen,ptr); //write data memcpy(data+ptr,this->data,datalen); ptr+=datalen; //add padding... pad(data,ptr+padding,ptr); return ptr; } void ArtDataObject::GetString(wchar_t * str, int len) { if(rev4(data+4) != 2) { str[0]=0; return; }//not utf-16! int l = (rev4(data)/sizeof(wchar_t)); StringCchCopyN(str, len, (wchar_t*)&data[12], l); //lstrcpyn(str,(wchar_t*)&data[12],min(l,len)); } void ArtDataObject::SetString(wchar_t * str) { SAFEFREE(data); datalen = wcslen(str)*sizeof(wchar_t) + 12; data = (BYTE*)malloc(datalen); rev4(wcslen(str)*sizeof(wchar_t),data); rev4(2,data+4); //type 2 means utf-16 rev4(0,data+8); //unk memcpy(data+12,str,wcslen(str)*sizeof(wchar_t)); } /////////////////////////////////////////////////////////////////////////////// // ArtImageName ArtImageName::ArtImageName() : headerlen(0x4c), totallen(0), corrid(0), ithmboffset(0), imagesize(0), vpad(0), hpad(0), imgh(0), imgw(0), filename(0) { } ArtImageName::~ArtImageName() { SAFEDELETE(filename); } int ArtImageName::parse(BYTE *data, int len) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhni",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x4c) return -1; totallen = rev4i(data,ptr); int children = rev4i(data,ptr); corrid = rev4i(data,ptr); ithmboffset = rev4i(data,ptr); imagesize = rev4i(data,ptr); vpad = (short)rev2i(data,ptr); hpad = (short)rev2i(data,ptr); imgw = rev2i(data,ptr); imgh = rev2i(data,ptr); ptr = headerlen; if(children) { filename = new ArtDataObject(); int p = filename->parse(data+ptr,len-ptr); if(p<0) SAFEDELETE(filename); } return totallen; } int ArtImageName::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhni",data,ptr); rev4i(headerlen,data,ptr); rev4i(0,data,ptr); // fill in totallen later rev4i(filename?1:0,data,ptr); //num children rev4i(corrid,data,ptr); rev4i(ithmboffset,data,ptr); rev4i(imagesize,data,ptr); rev2i(vpad,data,ptr); rev2i(hpad,data,ptr); rev2i(imgw,data,ptr); rev2i(imgh,data,ptr); pad(data,headerlen,ptr); if(filename) { int p = filename->write(data+ptr,len-ptr); if(p<0) return -1; ptr+=p; } rev4(ptr,&data[8]); // fill in totallen return ptr; } /////////////////////////////////////////////////////////////////////////////// // ArtAlbumList ArtAlbumList::ArtAlbumList() : headerlen(0x5c) { } ArtAlbumList::~ArtAlbumList() {} int ArtAlbumList::parse(BYTE *data, int len) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhla",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x5c) return -1; int children = rev4i(data,ptr); if(children != 0) return -1; return headerlen; } int ArtAlbumList::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhla",data,ptr); rev4i(headerlen,data,ptr); rev4i(0,data,ptr); // num children pad(data,headerlen,ptr); return ptr; } /////////////////////////////////////////////////////////////////////////////// // ArtFileList ArtFileList::ArtFileList() : headerlen(0x5c) { } ArtFileList::~ArtFileList() { for (auto file : files) { delete file; } files.clear(); } int ArtFileList::parse(BYTE *data, int len) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhlf",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x5c) return -1; int children = rev4i(data,ptr); ptr = headerlen; for(int i=0; iparse(data+ptr,len-ptr); if(p<0) { delete f; return -1; } ptr+=p; files.push_back(f); } return ptr; } int ArtFileList::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhlf",data,ptr); rev4i(headerlen,data,ptr); rev4i(files.size(),data,ptr); // num children pad(data,headerlen,ptr); for(auto f = files.begin(); f != files.end(); f++) { int p = (*f)->write(data+ptr,len-ptr); if(p<0) return -1; ptr+=p; } return ptr; } ArtFile * ArtFileList::getFile(int corrid) { for(auto i = files.begin(); i!=files.end(); i++) { if((*i)->corrid == corrid) return *i; } return NULL; } /////////////////////////////////////////////////////////////////////////////// // ArtFile ArtFile::ArtFile() : headerlen(0x7c), corrid(0), imagesize(0), file(0) { } ArtFile::~ArtFile() { SAFEFREE(file); } int ArtFile::parse(BYTE *data, int len) { int ptr=4; if(len < headerlen) return -1; if (_strnicmp((char *)data,"mhif",4)) return -1; headerlen = rev4i(data,ptr); if(headerlen < 0x7c) return -1; int totallen = rev4i(data,ptr); rev4i(data,ptr); // might not be numchildren, it's really unk1 corrid = rev4i(data,ptr); imagesize = rev4i(data,ptr); return totallen; } int ArtFile::write(BYTE *data, int len) { int ptr=0; if(headerlen > len) return -1; putmh("mhif",data,ptr); rev4i(headerlen,data,ptr); rev4i(0,data,ptr); // total len, fill in later rev4i(0,data,ptr); // numchildren/unk1 rev4i(corrid,data,ptr); rev4i(imagesize,data,ptr); pad(data,headerlen,ptr); // write children, if we had any... rev4(ptr,&data[8]); // fill in total len return ptr; } struct ArtFileImageSort { bool operator()(ArtFileImage*& ap,ArtFileImage*& bp) { return ap->start < bp->start; } }; void ArtFile::sortImages() { std::sort(images.begin(),images.end(),ArtFileImageSort()); for(size_t i = 1; i != images.size(); i++) { if(images[i]->start == images[i-1]->start) { images.erase(images.begin() + i); i--; images[i]->refcount++; } } } size_t ArtFile::getNextHole(size_t size) { size_t s=0; for(auto i = images.begin(); i!=images.end(); i++) { if((*i)->start - s >= size) return s; s = (*i)->start + (*i)->len; } return s; } bool writeDataToThumb(wchar_t *file, unsigned short * data, int len) { FILE * f = _wfopen(file,L"ab"); if(!f) return false; fwrite(data,len,sizeof(short),f); fclose(f); return true; }