Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
914
Src/Plugins/Portable/pmp_ipod/iPodArtworkDB.cpp
Normal file
914
Src/Plugins/Portable/pmp_ipod/iPodArtworkDB.cpp
Normal file
|
@ -0,0 +1,914 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue