915 lines
22 KiB
C++
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;
|
||
|
}
|