winamp/Src/Plugins/Portable/pmp_ipod/iPodDB.h
2024-09-24 14:54:57 +02:00

1256 lines
38 KiB
C++

/*
*
*
* Copyright (c) 2004 Samuel Wood (sam.wood@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.
*
*
*
*/
// For more information on how all this stuff works, see:
// http://www.ipodlinux.org/ITunesDB
// iPodDB.h: interface for the iPod classes.
//
//////////////////////////////////////////////////////////////////////
#ifndef __IPODDB_H__
#define __IPODDB_H__
#pragma once
#pragma warning( disable : 4786)
#include <algorithm>
#include <windows.h>
#include <bfc/platform/types.h>
#include <map>
#include <vector>
#ifdef _DEBUG
#undef ASSERT
#define ASSERT(x) assert(x)
#else
#define ASSERT(x) {}
#endif
// mhod types
#define MHOD_TITLE 1
#define MHOD_LOCATION 2
#define MHOD_ALBUM 3
#define MHOD_ARTIST 4
#define MHOD_GENRE 5
#define MHOD_FILETYPE 6
#define MHOD_EQSETTING 7
#define MHOD_COMMENT 8
#define MHOD_CATEGORY 9 // iTunes Music Store Podcast category
#define MHOD_COMPOSER 12
#define MHOD_GROUPING 13
#define MHOD_DESCRIPTION 14 // Podcast show notes text - accessible via the center iPod button
#define MHOD_ENCLOSUREURL 15 // Used by iTunes 4.9 for a Podcast's original enclosure URL
#define MHOD_RSSFEEDURL 16 // Used by iTunes 4.9 for a Podcast's RSS 2.0 feed URL
#define MHOD_CHAPTER 17 // M4A-style tagged data that is used to support subsongs/chapters
#define MHOD_SUBTITLE 18
#define MHOD_SHOW 19
#define MHOD_EPISODE 20
#define MHOD_TVNETWORK 21
#define MHOD_ALBUMARTIST 22
#define MHOD_ARTIST_SORT 23
#define MHOD_TITLE_SORT 27
#define MHOD_ALBUM_SORT 28
#define MHOD_ALBUMARTIST_SORT 29
#define MHOD_COMPOSER_SORT 30
#define MHOD_SHOW_SORT 31
#define MHOD_SPLPREF 50
#define MHOD_SPLDATA 51
#define MHOD_LIBRARY 52 // Found in the default hidden playlist
#define MHOD_LIBRARY_LETTER 53 // letter jump table
#define MHOD_PLAYLIST 100
#define MHOD_ALBUMLIST_ALBUM 200
#define MHOD_ALBUMLIST_ARTIST 201
#define MHOD_ALBUMLIST_ARTIST_SORT 202
#define MHOD_ALBUMLIST_PODCASTURL 203
#define MHOD_ALBUMLIST_SHOW 204
// Equalizer defines
#define EQ_NONE -1
#define EQ_ACOUSTIC 100
#define EQ_BASSBOOSTER 101
#define EQ_BASSREDUCER 102
#define EQ_CLASSICAL 103
#define EQ_DANCE 104
#define EQ_DEEP 105
#define EQ_ELECTRONIC 106
#define EQ_FLAT 107
#define EQ_HIPHOP 108
#define EQ_JAZZ 109
#define EQ_LATIN 110
#define EQ_LOUDNESS 111
#define EQ_LOUNGE 112
#define EQ_PIANO 113
#define EQ_POP 114
#define EQ_RNB 115
#define EQ_ROCK 116
#define EQ_SMALLSPEAKERS 117
#define EQ_SPOKENWORD 118
#define EQ_TREBLEBOOSTER 119
#define EQ_TREBLEREDUCER 120
#define EQ_VOCALBOOSTER 121
// Smart Playlist stuff
#define SPLMATCH_AND 0 // AND rule - all of the rules must be true in order for the combined rule to be applied
#define SPLMATCH_OR 1 // OR rule
// Limit Types.. like limit playlist to 100 minutes or to 100 songs
#define LIMITTYPE_MINUTES 0x01
#define LIMITTYPE_MB 0x02
#define LIMITTYPE_SONGS 0x03
#define LIMITTYPE_HOURS 0x04
#define LIMITTYPE_GB 0x05
// Limit Sorts.. Like which songs to pick when using a limit type
// Special note: the values for LIMITSORT_LEAST_RECENTLY_ADDED, LIMITSORT_LEAST_OFTEN_PLAYED,
// LIMITSORT_LEAST_RECENTLY_PLAYED, and LIMITSORT_LOWEST_RATING are really 0x10, 0x14,
// 0x15, 0x17, with the 'limitsort_opposite' flag set. This is the same value as the
// "positive" value (i.e. LIMITSORT_LEAST_RECENTLY_ADDED), and is really very terribly
// awfully weird, so we map the values to iPodDB specific values with the high bit set.
//
// On writing, we check the high bit and write the limitsort_opposite from that. That
// way, we don't have to deal with programs using the class needing to set the wrong
// limit and then make it into the "opposite", which would be frickin' annoying.
#define LIMITSORT_RANDOM 0x02
#define LIMITSORT_SONG_NAME 0x03
#define LIMITSORT_ALBUM 0x04
#define LIMITSORT_ARTIST 0x05
#define LIMITSORT_GENRE 0x07
#define LIMITSORT_MOST_RECENTLY_ADDED 0x10
#define LIMITSORT_COMPOSER 0x12 // Not used by iTunes, but inferred from the Type 52 MHOD's Composer type
#define LIMITSORT_LEAST_RECENTLY_ADDED 0x80000010 // See note above
#define LIMITSORT_MOST_OFTEN_PLAYED 0x14
#define LIMITSORT_LEAST_OFTEN_PLAYED 0x80000014 // See note above
#define LIMITSORT_MOST_RECENTLY_PLAYED 0x15
#define LIMITSORT_LEAST_RECENTLY_PLAYED 0x80000015 // See note above
#define LIMITSORT_HIGHEST_RATING 0x17
#define LIMITSORT_LOWEST_RATING 0x80000017 // See note above
// Smartlist Actions - Used in the rules.
/*
really this is a bitmapped field...
high byte
bit 0 = "string" values if set, "int" values if not set
bit 1 = "not", or to negate the check.
lower 2 bytes
bit 0 = simple "IS" query
bit 1 = contains
bit 2 = begins with
bit 3 = ends with
bit 4 = greater than
bit 5 = unknown, but probably greater than or equal to
bit 6 = less than
bit 7 = unknown, but probably less than or equal to
bit 8 = a range selection
bit 9 = "in the last"
*/
#define SPLACTION_IS_INT 0x00000001 // Also called "Is Set" in iTunes
#define SPLACTION_IS_GREATER_THAN 0x00000010 // Also called "Is After" in iTunes
#define SPLACTION_IS_LESS_THAN 0x00000040 // Also called "Is Before" in iTunes
#define SPLACTION_IS_IN_THE_RANGE 0x00000100
#define SPLACTION_IS_IN_THE_LAST 0x00000200
#define SPLACTION_BINARY_AND 0x00000400
#define SPLACTION_IS_STRING 0x01000001
#define SPLACTION_CONTAINS 0x01000002
#define SPLACTION_STARTS_WITH 0x01000004
#define SPLACTION_ENDS_WITH 0x01000008
#define SPLACTION_IS_NOT_INT 0x02000001 // Also called "Is Not Set" in iTunes
#define SPLACTION_IS_NOT_GREATER_THAN 0x02000010 // Note: Not available in iTunes
#define SPLACTION_IS_NOT_LESS_THAN 0x02000040 // Note: Not available in iTunes
#define SPLACTION_IS_NOT_IN_THE_RANGE 0x02000100 // Note: Not available in iTunes
#define SPLACTION_IS_NOT_IN_THE_LAST 0x02000200
#define SPLACTION_UNKNOWN2 0x02000800
#define SPLACTION_IS_NOT 0x03000001
#define SPLACTION_DOES_NOT_CONTAIN 0x03000002
#define SPLACTION_DOES_NOT_START_WITH 0x03000004 // Note: Not available in iTunes
#define SPLACTION_DOES_NOT_END_WITH 0x03000008 // Note: Not available in iTunes
// these are to pass to AddRule() when you need a unit for the two "in the last" action types
// Or, in theory, you can use any time range... iTunes might not like it, but the iPod might.
#define SPLACTION_LAST_DAYS_VALUE 86400 // number of seconds in 24 hours
#define SPLACTION_LAST_WEEKS_VALUE 604800 // number of seconds in 7 days
#define SPLACTION_LAST_MONTHS_VALUE 2628000 // number of seconds in 30.4167 days ~= 1 month
// Hey, why limit ourselves to what iTunes can do? If the iPod can deal with it, excellent!
#define SPLACTION_LAST_SECONDS_RULE 1 // one second
#define SPLACTION_LAST_HOURS_VALUE 3600 // number of seconds in 1 hour
#define SPLACTION_LAST_MINUTES_VALUE 60 // number of seconds in 1 minute
#define SPLACTION_LAST_YEARS_VALUE 31536000 // number of seconds in 365 days
// fun ones.. Near as I can tell, all of these work. It's open like that. :)
#define SPLACTION_LAST_LUNARCYCLE_VALUE 2551443 // a "lunar cycle" is the time it takes the moon to circle the earth
#define SPLACTION_LAST_SIDEREAL_DAY 86164 // a "sidereal day" is time in one revolution of earth on its axis
#define SPLACTION_LAST_SWATCH_BEAT 86 // a "swatch beat" is 1/1000th of a day.. search for "internet time" on google
#define SPLACTION_LAST_MOMENT 90 // a "moment" is 1/40th of an hour, or 1.5 minutes
#define SPLACTION_LAST_OSTENT 600 // an "ostent" is 1/10th of an hour, or 6 minutes
#define SPLACTION_LAST_FORTNIGHT 1209600 // a "fortnight" is 14 days
#define SPLACTION_LAST_VINAL 1728000 // a "vinal" is 20 days
#define SPLACTION_LAST_QUARTER 7889231 // a "quarter" is a quarter year
#define SPLACTION_LAST_SOLAR_YEAR 31556926 // a "solar year" is the time it takes the earth to go around the sun
#define SPLACTION_LAST_SIDEREAL_YEAR 31558150 // a "sidereal year" is the time it takes the earth to reach the same point in space again, compared to the stars
// Smartlist fields - Used for rules.
#define SPLFIELD_SONG_NAME 0x02 // String
#define SPLFIELD_ALBUM 0x03 // String
#define SPLFIELD_ARTIST 0x04 // String
#define SPLFIELD_BITRATE 0x05 // Int (e.g. from/to = 128)
#define SPLFIELD_SAMPLE_RATE 0x06 // Int (e.g. from/to = 44100)
#define SPLFIELD_YEAR 0x07 // Int (e.g. from/to = 2004)
#define SPLFIELD_GENRE 0x08 // String
#define SPLFIELD_KIND 0x09 // String
#define SPLFIELD_DATE_MODIFIED 0x0a // Int/Mac Timestamp (e.g. from/to = bcf93280 == is before 6/19/2004)
#define SPLFIELD_TRACKNUMBER 0x0b // Int (e.g. from = 1, to = 2)
#define SPLFIELD_SIZE 0x0c // Int (e.g. from/to = 0x00600000 for 6MB)
#define SPLFIELD_TIME 0x0d // Int (e.g. from/to = 83999 for 1:23/83 seconds)
#define SPLFIELD_COMMENT 0x0e // String
#define SPLFIELD_DATE_ADDED 0x10 // Int/Mac Timestamp (e.g. from/to = bcfa83ff == is after 6/19/2004)
#define SPLFIELD_COMPOSER 0x12 // String
#define SPLFIELD_PLAYCOUNT 0x16 // Int (e.g. from/to = 1)
#define SPLFIELD_LAST_PLAYED 0x17 // Int/Mac Timestamp (e.g. from = bcfa83ff (6/19/2004), to = 0xbcfbd57f (6/20/2004))
#define SPLFIELD_DISC_NUMBER 0x18 // Int (e.g. from/to = 1)
#define SPLFIELD_RATING 0x19 // Int/Stars Rating (e.g. from/to = 60 (3 stars))
#define SPLFIELD_COMPILATION 0x1f // Int (e.g. is set -> SPLACTION_IS_INT/from=1, is not set -> SPLACTION_IS_NOT_INT/from=1)
#define SPLFIELD_BPM 0x23 // Int (e.g. from/to = 60)
#define SPLFIELD_GROUPING 0x27 // String
#define SPLFIELD_PLAYLIST 0x28 // XXX - Unknown...not parsed correctly...from/to = 0xb6fbad5f for "Purchased Music". Extra data after "to"...
#define SPLFIELD_VIDEO_KIND 0x3C // Logic Int (???)
#define SPLFIELD_TVSHOW 0x3E // Int
#define SPLFIELD_SEASON_NR 0x3F // Int
#define SPLFIELD_SKIPCOUNT 0x44 // Int
#define SPLFIELD_ALBUMARTIST 0x47 // string
#define SPLDATE_IDENTIFIER 0x2dae2dae2dae2dae
// MHOD Type 52 types
#define TYPE52_SONG_NAME 0x03
#define TYPE52_ARTIST 0x05
#define TYPE52_ALBUM 0x04
#define TYPE52_GENRE 0x07
#define TYPE52_COMPOSER 0x12
static const uint32_t FILETYPE_M4A=0x4d344120;
static const uint32_t FILETYPE_MP3=0x4d503320;
static const uint32_t FILETYPE_WAV=0x57415620;
// useful functions
time_t mactime_to_wintime (const unsigned long mactime);
unsigned long wintime_to_mactime (const __time64_t time);
char * UTF16_to_char(wchar_t * str, int length);
// Pre-declare iPod_* classes
class iPod_mhbd;
class iPod_mhsd;
class iPod_mhlt;
class iPod_mhit;
class iPod_mhlp;
class iPod_mhyp;
class iPod_slst;
class iPod_mhip;
class iPod_mhod;
class iPod_mqed;
class iPod_mhpo;
class iPod_pqed;
class iPod_mhla;
// Maximum string length that iTunes writes to the database
#define SPL_MAXSTRINGLENGTH 255
// a struct to hold smart playlist rules in mhods
struct SPLRule
{
SPLRule() :
field(0),
action(0),
length(0),
fromvalue(0),
fromdate(0),
fromunits(0),
tovalue(0),
todate(0),
tounits(0),
unk1(0),
unk2(0),
unk3(0),
unk4(0),
unk5(0)
{
memset(string, 0, sizeof(string));
}
void SetString(const wchar_t *value)
{
if(value)
{
lstrcpynW(string, value, SPL_MAXSTRINGLENGTH);
length = lstrlenW(string) * 2;
}
else
{
memset(string, 0, sizeof(string));
length = 0;
}
}
unsigned long field;
unsigned long action;
unsigned long length;
wchar_t string[SPL_MAXSTRINGLENGTH + 1];
// from and to are pretty stupid.. if it's a date type of field, then
// value = 0x2dae2dae2dae2dae,
// date = some number, like 2 or -2
// units = unit in seconds, like 604800 = a week
// but if this is actually some kind of integer comparison, like rating = 60 (3 stars)
// value = the value we care about
// date = 0
// units = 1
// So we leave these as they are, and will just deal with it in the rules functions.
uint64_t fromvalue;
int64_t fromdate;
uint64_t fromunits;
uint64_t tovalue;
int64_t todate;
uint64_t tounits;
unsigned long unk1;
unsigned long unk2;
unsigned long unk3;
unsigned long unk4;
unsigned long unk5;
};
// PCEntry: Play Count struct for the entries in iPod_mhdp
struct PCEntry
{
unsigned long playcount;
unsigned long lastplayedtime;
unsigned long bookmarktime;
unsigned long stars;
uint32_t unk1;
uint32_t skipcount;
uint32_t skippedtime;
};
/**************************************
iTunesDB Database Layout
MHBD (Database)
|
|-MHSD (Data Set)
| |
| |-MHLT (Track List)
| | |
| | |-MHIT (Track Item)
| | | |
| | | |-MHOD (Description Object)
| | | |-MHOD
| | | | ...
| | |
| | |-MHIT
| | | |
| | | |-MHOD
| | | |-MHOD
| | | | ...
| | |
| | |-...
|
|
|-MHSD
| |
| |-MHLP (Playlists List)
| | |
| | |-MHYP (Playlist)
| | | |
| | | |-MHOD
| | | |-MHIP (Playlist Item)
| | | | ...
| | |
| | |-MHYP
| | | |
| | | |-MHOD
| | | |-MHIP
| | | | ...
| | |
| | |-...
**************************************/
// base class, not used directly
class iPodObj
{
public:
iPodObj();
virtual ~iPodObj();
// parse function is required in all subclasses
// feed it a iTunesDB, it creates an object hierarchy
virtual long parse(const uint8_t *data) = 0;
// write function is required too
// feed it a buffer and the size of the buffer, it fills it with an iTunesDB
// return value is size of the resulting iTunesDB
// return of -1 means the buffer was too small
virtual long write(uint8_t * data, const unsigned long datasize) = 0;
unsigned long size_head;
unsigned long size_total;
};
// MHBD: The database - parent of all items
class iPod_mhbd : public iPodObj
{
public:
iPod_mhbd();
virtual ~iPod_mhbd();
virtual long parse(const uint8_t *data);
virtual long write(uint8_t * data, const unsigned long datasize);
virtual long write(uint8_t * data, const unsigned long datasize, uint8_t * fwid);
uint32_t unk1;
uint32_t dbversion;
uint32_t children;
uint64_t id;
uint16_t platform;
uint16_t language;
uint64_t library_id;
uint32_t unk80;
uint32_t unk84;
int32_t timezone; // in seconds
uint16_t audio_language;
uint16_t subtitle_language;
uint16_t unk164;
uint16_t unk166;
uint16_t unk168;
iPod_mhsd *mhsdsongs;
iPod_mhsd *mhsdplaylists;
iPod_mhsd *mhsdsmartplaylists;
};
// MHSD: List container - parent of MHLT or MHLP, child of MHBD
class iPod_mhsd : public iPodObj
{
public:
iPod_mhsd();
iPod_mhsd(int newindex);
virtual ~iPod_mhsd();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize) {return write(data,datasize,1);}
virtual long write(unsigned char * data, const unsigned long datasize, int index);
uint32_t index; // 1 = mhlt, 3 = mhlp, 2 = legacy mhlp, 4 = album list, 5 = mhlp_smart
iPod_mhlt * mhlt;
iPod_mhlp * mhlp;
iPod_mhlp * mhlp_smart;
iPod_mhla * mhla;
};
class iPod_mhia : public iPodObj
{
public:
iPod_mhia();
virtual ~iPod_mhia();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
uint16_t unk1;
uint16_t albumid;
uint64_t dbid;
uint32_t type;
std::vector<iPod_mhod*> mhod;
};
class ArtistAlbumPair
{
public:
const wchar_t* artist;
const wchar_t* album;
ArtistAlbumPair() : artist(0), album(0) {}
ArtistAlbumPair(const wchar_t* artist, const wchar_t* album) : artist(artist), album(album) {}
/*bool operator < (const ArtistAlbumPair& that) const
{
int yy = _wcsicmp(artist, that.artist);
if(yy) return yy < 0;
return _wcsicmp(album, that.album) < 0;
}*/
};
struct ArtistAlbumPairComparer
{
int operator ()(const ArtistAlbumPair &a, const ArtistAlbumPair &b) const
{
int yy = _wcsicmp(a.artist, b.artist);
if(yy) return yy;
return _wcsicmp(a.album, b.album);
}
};
class iPod_mhla : public iPodObj
{
public:
iPod_mhla();
virtual ~iPod_mhla();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
uint16_t GetAlbumId(const wchar_t* artist, const wchar_t* album);
void ClearAlbumsList();
//typedef std::map<ArtistAlbumPair, uint16_t> albums_map_t;
typedef std::map<ArtistAlbumPair, uint16_t, ArtistAlbumPairComparer> albums_map_t;
albums_map_t albums;
uint16_t nextAlbumId;
};
// MHLT: song list container - parent of MHIT, child of MHSD
class iPod_mhlt : public iPodObj
{
public:
typedef std::map<uint32_t, iPod_mhit*> mhit_map_t;
//typedef std::map<unsigned long, iPod_mhit*> mhit_map_t; // Map the unique mhit.id to a mhit object
typedef mhit_map_t::value_type mhit_value_t;
iPod_mhlt();
virtual ~iPod_mhlt();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
const unsigned long GetChildrenCount() const { return mhit.size(); }
// returns a pointer to the new iPod_mhit object in the track list, which you edit directly
iPod_mhit *NewTrack();
void AddTrack(iPod_mhit *new_track);
// takes a position index, returns a pointer to the track itself, or NULL if the index isn't found.
iPod_mhit *GetTrack(uint32_t index) const;
// searches for a track based on the track's id number (mhit.id). returns mhit pointer, or NULL if the id isn't found.
iPod_mhit * GetTrackByID(const unsigned long id);
// couple of ways to delete a track
bool DeleteTrack(const unsigned long index);
bool DeleteTrackByID(const unsigned long id);
// clears out the tracklist
bool ClearTracks(const bool clearMap = true);
// the map of the tracks themselves
mhit_map_t mhit;
std::vector<uint32_t> mhit_indexer;
uint32_t GetNextID();
private:
volatile uint32_t next_mhit_id;
};
// MHIT: song item - parent of MHOD, child of MHLT
class iPod_mhit : public iPodObj
{
public:
iPod_mhit();
virtual ~iPod_mhit();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
const unsigned long GetChildrenCount() const { return(mhod.size()); }
// will add a new mhod string to the mhit
// optional: pass in a type to get an existing string, if there is one,
// or a new one with the type filled in already, if there is not one
iPod_mhod * AddString(const int type=0);
// Find a string by type
iPod_mhod * FindString(const unsigned long type) const;
// deletes a string from the track
// if more than one string of given type exists, all of that type will be deleted,
// to ensure consistency. Pointers to these strings will be invalid after this.
// return val is how many strings were deleted
unsigned long DeleteString(const unsigned long type);
// Creates a copy of the mhit. The operator = is overloaded so you can
// more easily copy mhit's.
static void Duplicate(const iPod_mhit *src, iPod_mhit *dst);
iPod_mhit& operator=(const iPod_mhit& src);
int GetRating() { return stars/20; }
void SetRating(int rating) { stars=rating*20; }
int GetEQSetting();
void SetEQSetting(int value);
unsigned int GetFileTypeID(const wchar_t *filename);
uint32_t id;
uint32_t visible; // 0x01 means the song shows up on the iPod, all other values means it is hidden
uint32_t filetype; // MP3 = 0x4d503320, M4A = 0x4d344120, M4B = 0x4d344220, M4P = 0x4d345020, WAV = 0x57415620, AA = ???
uint8_t vbr;
uint8_t type;
uint8_t compilation;
uint8_t stars;
uint32_t lastmodifiedtime; // iTunes sets this the UTC time value for the Windows Last Modified timestamp
uint32_t size;
uint32_t length;
uint32_t tracknum;
uint32_t totaltracks;
uint32_t year;
uint32_t bitrate;
uint16_t samplerate;
uint16_t samplerate_fixedpoint;
uint32_t volume;
uint32_t starttime;
uint32_t stoptime;
uint32_t soundcheck;
uint32_t playcount;
uint32_t playcount2; // Seems to always be the same as playcount(?!?)
uint32_t lastplayedtime;
uint32_t cdnum;
uint32_t totalcds;
uint32_t userID; // Apple Store User ID
uint32_t addedtime; // iTunes sets this to the UTC time value for when the file was added to the iTunes library
uint32_t bookmarktime;
uint64_t dbid; // 64 bit value that identifies this mhit across iPod databases. iTunes increments this by 1 for each additional song. (previously unk7 and unk8)
uint32_t BPM;
uint32_t app_rating; // The rating set by the application, as opposed to the rating set on the iPod itself
uint8_t checked; // a "checked" song has the value of 0, a non-checked song is 1
uint16_t unk9; // Seems to always be 0xffff...
uint16_t artworkcount; // Number of artwork files attached to this song
uint32_t artworksize; // Size of all artwork files attached to this song, in bytes. (was unk10);
uint32_t unk11;
float samplerate2;
uint32_t releasedtime;
uint32_t unk14;
uint32_t unk15;
uint32_t unk16;
/* --- */
uint32_t skipcount;
uint32_t skippedtime;
uint8_t hasArtwork;
uint8_t skipShuffle;
uint8_t rememberPosition;
uint8_t unk19;
uint64_t dbid2; // same as dbid?
uint8_t lyrics_flag;
uint8_t movie_flag;
uint8_t mark_unplayed;
uint8_t unk20;
uint32_t unk21;
uint32_t pregap;
uint64_t samplecount;
uint32_t unk25;
uint32_t postgap;
uint32_t unk27;
uint32_t mediatype;
uint32_t seasonNumber;
uint32_t episodeNumber;
uint32_t unk31;
uint32_t unk32;
uint32_t unk33;
uint32_t unk34;
uint32_t unk35;
uint32_t unk36;
/* --- */
uint32_t unk37;
uint32_t gaplessData;
uint32_t unk39;
uint16_t albumgapless;
uint16_t trackgapless;
uint32_t unk40;
uint32_t unk41;
uint32_t unk42;
uint32_t unk43;
uint32_t unk44;
uint32_t unk45;
uint32_t unk46;
uint32_t album_id;
uint32_t unk48;
uint32_t unk49;
uint32_t unk50;
uint32_t unk51;
uint32_t unk52;
uint32_t unk53;
uint32_t unk54;
uint32_t unk55;
uint32_t unk56;
/* --- */
// 22 bytes of unknown (we'll just write back zeroes)
uint32_t mhii_link; // TODO: benski> figure this thing out
// 32 more bytes of unknown (we'll just write back zeroes)
/* benski> this is a hack. i'm putting this in here so we can retrieve album art from the transfer thread and add it in the main thread
it doesn't really belong as part of this object, though! */
// protect these members, so stuff doesn't fuck up my cache
protected:
std::vector<iPod_mhod*> mhod;
iPod_mhod * mhodcache[25];
};
// MHLP: playlist container - parent of MHYP, child of MHSD
// Important note: Playlist zero must always be the default playlist, containing every
// track in the DB. To do this, always call "GetDefaultPlaylist()" before you create any
// other playlists, if you start from scratch.
// After you're done adding/deleting tracks in the database, and just before you call
// write(), do the following: GetDefaultPlaylist()->PopulatePlaylist(ptr_to_mhlt);
class iPod_mhlp : public iPodObj
{
public:
iPod_mhlp();
virtual ~iPod_mhlp();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize) {return write(data,datasize,3);}
virtual long write(unsigned char * data, const unsigned long datasize, int index);
const unsigned long GetChildrenCount() const { return mhyp.size(); }
// returns a new playlist for you
iPod_mhyp * AddPlaylist();
// gets a playlist
iPod_mhyp * GetPlaylist(const unsigned long pos) const { return mhyp.at(pos); }
// finds a playlist by its ID
iPod_mhyp * FindPlaylist(const uint64_t playlistID);
// deletes the playlist at a position
bool DeletePlaylist(const unsigned long pos);
// deletes the playlist matching the ID
bool DeletePlaylistByID(const uint64_t playlistID);
// gets the default playlist ( GetPlaylist(0); )
// if there are no playlists yet (empty db), then it creates the default playlist
// and returns a pointer to it
iPod_mhyp * GetDefaultPlaylist();
// erases all playlists, including the default one, so be careful here.
// Set createDefaultPlaylist to create a new, empty default playlist
bool ClearPlaylists(const bool createDefaultPlaylist = false);
// Goes through all playlists and removed any songs that are no longer in the MHLT
void RemoveDeadPlaylistEntries(iPod_mhlt *mhlt);
std::vector<iPod_mhyp*> mhyp;
void SortPlaylists();
private:
bool beingDeleted;
};
int STRCMP_NULLOK(const wchar_t *pa, const wchar_t *pb);
// MHYP: playlist - parent of MHOD or MHIP, child of MHLP
class iPod_mhyp : public iPodObj
{
public:
iPod_mhyp();
virtual ~iPod_mhyp();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize) {return write(data,datasize,3);}
virtual long write(unsigned char * data, const unsigned long datasize, int index);
bool IsSmartPlaylist(void) const { return(isSmartPlaylist); }
// add an entry to the playlist. Creates a new entry, returns the position in the vector
// optionally fills in the songindex for you, with the ID from a track you might have
long AddPlaylistEntry(iPod_mhip * entry, const unsigned long id=0);
// give it a song id, it'll return a position in the playlist
// -1, as always, means not found
// if the same entry is in the playlist multiple times, this only gives back the first one
long FindPlaylistEntry(const unsigned long id) const;
// get an mhip given its position
iPod_mhip * GetPlaylistEntry(const unsigned long pos) const { return mhip.at(pos); }
// deletes an entry from the playlist. Pointers to that entry become invalid
bool DeletePlaylistEntry(const unsigned long pos);
// Removes all playlist entries matching the songindex parameter
bool DeletePlaylistEntryByID(unsigned long songindex);
// clears a playlist of all mhip entries
bool ClearPlaylist();
// populates a playlist to be the same as a track list you pass into it.
// Mainly only useful for building the default playlist after you add/delete tracks
// GetDefaultPlaylist()->PopulatePlaylist(ptr_to_mhlt);
// for example...
long PopulatePlaylist(iPod_mhlt * tracks, int hidden_field=1);
// will add a new string to the playlist
// optional: pass in a type to get an existing string, if there is one,
// or a new one with the type filled in already, if there is not one
iPod_mhod * AddString(const int type=0);
// get an mhod given it's type.. Only really useful with MHOD_TITLE here, until
// smartlists get worked out better
iPod_mhod * FindString(const unsigned long type);
// deletes a string from the playlist
// if more than one string of given type exists, all of that type will be deleted,
// to ensure consistency. Pointers to these strings will be invalid after this.
// ret val is number of strings removed
unsigned long DeleteString(const unsigned long type);
void SetPlaylistTitle(const wchar_t *string);
const unsigned long GetMhodChildrenCount() const { return mhod.size(); }
const unsigned long GetMhipChildrenCount() const { return mhip.size(); }
static void Duplicate(iPod_mhyp *src, iPod_mhyp *dst);
unsigned long hidden;
unsigned long timestamp;
uint64_t playlistID; // ID of the playlist, used in smart playlist rules
unsigned long unk3;
unsigned short numStringMHODs;
unsigned short podcastflag;
unsigned long numLibraryMHODs;
std::vector<iPod_mhod*> mhod;
std::vector<iPod_mhip*> mhip;
struct indexMhit
{
__forceinline bool operator()(indexMhit*& one, indexMhit*& two)
{
#define RETIFNZ(x) { int yy = x; if(yy != 0) return yy < 0; }
//return(STRCMP_NULLOK(one->str.c_str(), two->str.c_str()) < 0 ? true : false);
RETIFNZ(STRCMP_NULLOK(one->str[0],two->str[0]));
RETIFNZ(STRCMP_NULLOK(one->str[1],two->str[1]));
RETIFNZ(STRCMP_NULLOK(one->str[2],two->str[2]));
RETIFNZ(one->track - two->track);
RETIFNZ(STRCMP_NULLOK(one->str[3],two->str[3]));
return true;
#undef RETIFNZ
}
unsigned int index;
const wchar_t *str[4];
int track;
};
iPod_mhlt::mhit_map_t *mhit;
std::vector<uint32_t> mhit_indexer;
bool writeLibraryMHODs;
bool operator()(iPod_mhyp*& one, iPod_mhyp*& two);
protected:
bool isSmartPlaylist;
bool isPopulated;
};
// MHIP: playlist item - child of MHYP
class iPod_mhip : public iPodObj
{
public:
iPod_mhip();
virtual ~iPod_mhip();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize) { return write(data,datasize,0); }
virtual long write(unsigned char * data, const unsigned long datasize, int entrynum);
static void Duplicate(iPod_mhip *src, iPod_mhip *dst);
unsigned long dataobjectcount; // was unk1
unsigned long podcastgroupflag; // was corrid
unsigned long groupid; // was unk2
unsigned long songindex;
unsigned long timestamp;
unsigned long podcastgroupref;
std::vector<iPod_mhod*> mhod;
};
// MHOD: string container item, child of MHIT or MHYP
// MHOD: string container item, child of MHIT or MHYP
class iPod_mhod : public iPodObj
{
public:
iPod_mhod();
virtual ~iPod_mhod();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
void SetString(const wchar_t *string);
static void Duplicate(iPod_mhod *src, iPod_mhod *dst);
static bool IsSimpleStringType(const unsigned int type);
uint32_t type;
uint32_t unk1;
uint32_t unk2;
// renamed this from corrid.. all it is is a position in the playlist
// for type 100 mhods that come immediately after mhips.
// for strings, this is the encoded type. 1 == UTF-16, 2 == UTF-8
union
{
uint32_t position;
uint32_t encoding_type;
};
uint32_t length;
uint32_t unk3;
uint32_t unk4;
// string mhods get the string put here, unaltered, still byte reversed
// Use unicode functions to work with this string.
wchar_t *str;
// mhod types 50 and up get the whole thing put here.
// until I can figure out all of these, I won't bother to try to recreate them
// and i'll just copy them back as needed when rewriting the iTunesDB file.
uint8_t * binary;
// stuff for type 50 mhod
uint8_t liveupdate; // "Live Updating" check box
uint8_t checkrules; // "Match X of the following conditions" check box
uint8_t checklimits; // "Limit To..." check box. 1 = checked, 0 = not checked
uint8_t matchcheckedonly; // "Match only checked songs" check box.
uint8_t limitsort_opposite; // Limit Sort rule is reversed (e.g. limitsort == LIMIT_HIGHEST_RATING really means LIMIT_LOWEST_RATING...quite weird...)
uint32_t limittype; // See Limit Types defines above
uint32_t limitsort; // See Limit Sort defines above
uint32_t limitvalue; // Whatever value you type next to "limit type".
// stuff for type 51 mhod
uint32_t unk5; // not sure, probably junk data
uint32_t rules_operator; // "All" (logical AND / value = 0) or "Any" (logical OR / value = 1).
std::vector<SPLRule*> rule;
bool parseSmartPlaylists;
};
// Smart Playlist. A smart playlist doesn't act different from a regular playlist,
// except that it contains a type 50 and type 51 MHOD. But deriving the iPod_slst
// class makes sense, since there are a lot of functions that are only appropriate
// for smart playlists, and it can guarantee that a type 50 and 51 MHOD will always
// be available.
class iPod_slst : public iPod_mhyp
{
public:
enum FieldType
{
ftString,
ftInt,
ftBoolean,
ftDate,
ftPlaylist,
ftUnknown,
ftBinaryAnd,
};
enum ActionType
{
atString,
atInt,
atBoolean,
atDate,
atRange,
atInTheLast,
atPlaylist,
atNone,
atInvalid,
atUnknown,
atBinaryAnd,
};
iPod_slst();
virtual ~iPod_slst();
iPod_mhod* GetPrefs(void) { UpdateMHODPointers(); return(splPref); }
void SetPrefs(const bool liveupdate = true, const bool rules_enabled = true, const bool limits_enabled = false,
const unsigned long limitvalue = 0, const unsigned long limittype = 0, const unsigned long limitsort = 0);
static FieldType GetFieldType(const unsigned long field);
static ActionType GetActionType(const unsigned long field, const unsigned long action);
static uint64_t ConvertDateValueToNum(const uint64_t val) { return(-(int64_t)val); }
static uint64_t ConvertNumToDateValue(const uint64_t val) { return(-(int64_t)val); }
// returns a pointer to the SPLDATA mhod
iPod_mhod* GetRules() { UpdateMHODPointers(); return(splData); }
// get the number of rules in the smart playlist
unsigned long GetRuleCount();
// Returns rule number (0 == first rule, -1 == error)
int AddRule(const unsigned long field,
const unsigned long action,
const wchar_t * string = NULL, // use string for string based rules
const uint64_t value = 0, // use value for single variable rules
const uint64_t from = 0, // use from and to for range based rules
const uint64_t to = 0,
const uint64_t units = 0); // use units for "in the last" based rules
int AddRule(const SPLRule& rule);
void RemoveAllRules(void);
// populates a smart playlist
// Pass in the mhlt with all the songs on the iPod, and it populates the playlist
// given those songs and the current rules
// Return value is number of songs in the resulting playlist.
long PopulateSmartPlaylist(iPod_mhlt * tracks, iPod_mhlp * playlists);
// used in PopulateSmartPlaylist
static bool EvalRule(
SPLRule * r,
iPod_mhit * track,
iPod_mhlt * tracks = NULL, // if you're going to allow playlist type rules
iPod_mhlp * playlists = NULL // these are required to be passed in
);
// Restore default prefs and remove all rules
void Reset(void);
protected:
void UpdateMHODPointers(void);
iPod_mhod *splPref;
iPod_mhod *splData;
};
// MHDP: Play Count class
class iPod_mhdp
{
public:
iPod_mhdp();
~iPod_mhdp();
unsigned long size_head;
unsigned long entrysize;
const unsigned long GetChildrenCount() const { return children; }
// return value is number of songs or -1 if error.
// you should probably check to make sure the number of songs is the same
// as the number of songs you read in from parsing the iTunesDB
virtual long parse(const uint8_t *data);
// there is no write() function because there is no conceivable need to ever write a
// play counts file.
const PCEntry &GetPlayCount(const unsigned int pos) const { return entry[pos]; }
// playcounts are stored in the Play Counts file, in the same order as the mhits are
// stored in the iTunesDB. So you should apply the changes from these entries to the
// mhits in order and then probably delete the Play Counts file entirely to prevent
// doing it more than once.
PCEntry *entry;
uint32_t children;
};
// MHPO: On-The-Go Playlist class
class iPod_mhpo
{
public:
iPod_mhpo();
virtual ~iPod_mhpo();
unsigned long size_head;
unsigned long unk1;
unsigned long unk2; // this looks like a timestamp, sorta
const unsigned long GetChildrenCount() const { return children; }
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
// This will create a new playlist from the OTGPlaylist..
// Give it the DB to create the playlist in and from, and a name for the playlist.
// Return value is a pointer to the playlist itself, which will be inside the DB you
// give to it as well.
// Returns NULL on error (can't create the playlist)
iPod_mhyp * CreatePlaylistFromOTG(iPod_mhbd * iPodDB, wchar_t * name);
// OTGPlaylists are stored in the OTGPlaylist file. When iTunes copies them into a
// new playlist, it deletes the file afterwards. I do not know if creating this file
// will make the iPod have an OTGPlaylist after you undock it. I added the write function
// anyway, in case somebody wants to try it. Not much use for it though, IMO.
uint32_t *idList;
uint32_t children;
};
// MQED: EQ Presets holder
class iPod_mqed
{
public:
iPod_mqed();
virtual ~iPod_mqed();
unsigned long size_head;
unsigned long unk1;
unsigned long unk2; // this looks like a timestamp, sorta
const unsigned long GetChildrenCount() const { return eqList.size(); }
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
std::vector<iPod_pqed*> eqList;
};
// PQED: A single EQ Preset
class iPod_pqed
{
public:
iPod_pqed();
virtual ~iPod_pqed();
unsigned long length; // length of name string
wchar_t * name; // name string
/*
10 band eq is not exactly what iTunes shows it to be.. It really is these:
32Hz, 64Hz, 128Hz, 256Hz, 512Hz, 1024Hz, 2048Hz, 4096Hz, 8192Hz, 16384Hz
Also note that although these are longs, The range is only -1200 to +1200. That's dB * 100.
*/
signed long preamp; // preamp setting
signed long eq[10]; // iTunes shows 10 bands for EQ presets
signed long short_eq[5]; // This is a 5 band version of the same thing (possibly what the iPod actually uses?)
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
};
struct iTunesStatsEntry
{
unsigned int GetBookmarkTimeInMilliseconds() { if(bookmarktime == 0xffffff) return(0); return(bookmarktime * 256); }
// These are 3 byte values
unsigned int entry_size;
unsigned int bookmarktime; // In 0.256 seconds units
unsigned int unk1; // Somehow associated with bookmark time
unsigned int unk2;
unsigned int playcount;
unsigned int skippedcount;
};
class iTunesStats
{
public:
iTunesStats();
~iTunesStats();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char * data, const unsigned long datasize);
const unsigned long GetChildrenCount() const { return children; }
const iTunesStatsEntry &GetEntry(const unsigned int pos) const { return entry[pos];}
// This is a 3 byte value
unsigned int unk1;
iTunesStatsEntry *entry;
uint32_t children;
iPod_mhlt *mhlt;
};
class iTunesShuffle
{
public:
iTunesShuffle();
~iTunesShuffle();
virtual long parse(const uint8_t *data);
virtual long write(unsigned char *data, const unsigned long datasize);
unsigned int GetChildrenCount() const { return numentries; }
unsigned int GetEntry(const unsigned int pos) const { return entry[pos]; }
//void AddEntry(const unsigned int index) { entry.push_back(index); }
void Randomize();
void Randomize(const unsigned int numsongs);
uint32_t *entry;
uint32_t numentries;
unsigned int datasize;
};
#endif