Archived
1
0
This repository has been archived on 2024-10-17. You can view files and clone it, but cannot push or open issues or pull requests.
winamp/Src/Plugins/Portable/pmp_ipod/iPodArtworkDB.cpp
2024-09-24 14:54:57 +02:00

915 lines
22 KiB
C++

/*
*
*
* 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 <algorithm>
#include <strsafe.h>
//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; i<numchildren; i++) {
ArtDataSet * d = new ArtDataSet;
int p = d->parse(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; i<children; i++) {
ArtImage * f = new ArtImage;
int p = f->parse(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; i<numchildren; i++) {
ArtDataObject *d = new ArtDataObject;
int p = d->parse(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<class T>
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; i<children; i++) {
ArtFile * f = new ArtFile;
int p = f->parse(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;
}