Index: src/Device.cs
===================================================================
--- src/Device.cs	(revision 149271)
+++ src/Device.cs	(working copy)
@@ -78,6 +78,12 @@
             }
         }
         
+        public bool SupportsSparseArtwork
+        {
+            // TODO: Grab exceptions from libgpod
+            get { return true; }
+        }
+        
         public SportKitManager SportKitManager {
             get {
                 if (sport_kit_manager == null) {
@@ -360,6 +366,8 @@
                 Console.WriteLine ("   {0}x{1} using {2}, rotated {3}", format.Width, 
                     format.Height, format.PixelFormat, format.Rotation);
             }
+            
+            Console.WriteLine ("  Supports Sparse Artwork: " + SupportsSparseArtwork);
         }
         
         #endregion
Index: src/Utility.cs
===================================================================
--- src/Utility.cs	(revision 149271)
+++ src/Utility.cs	(working copy)
@@ -85,5 +85,29 @@
                 return val;
             }
         }
+        
+        public static bool Equals(byte[] a, byte[] b) {
+            if (a.Length != b.Length)
+                return false;
+            
+            for (int i = 0; i < a.Length; i++) {
+                if (!a[i].Equals(b[i]))
+                    return false;
+            }
+            
+            return true;
+        }
+        
+        public static int Sum(byte[] a) {
+            if (a == null)
+                return 0;
+                
+            int sum = 0;
+            foreach (byte b in a) {
+                sum += b;
+            }
+            
+            return sum;
+        }
     }
 }
Index: src/Track.cs
===================================================================
--- src/Track.cs	(revision 149271)
+++ src/Track.cs	(working copy)
@@ -362,7 +362,11 @@
 
         private void FindCoverPhoto () {
             if (coverPhoto == null && db.ArtworkDatabase != null) {
-                coverPhoto = db.ArtworkDatabase.LookupPhotoByTrackId (record.DatabaseId);
+                if (record.RightSideArtworkId != 0)
+                    coverPhoto = db.ArtworkDatabase.LookupPhotoById (record.RightSideArtworkId);
+                
+                if (coverPhoto == null)
+                    coverPhoto = db.ArtworkDatabase.LookupPhotoByTrackId (record.DatabaseId);
             }
         }
 
@@ -415,12 +419,18 @@
             record.HasArtwork = true;
             record.ArtworkCount = 1; // this is actually for artwork in mp3 tags, but it seems to be needed anyway
         }
+        
+        public void ShareCoverArt (Track track) {
+            RemoveCoverArt();
+            record.RightSideArtworkId = track.Record.RightSideArtworkId;
+        }
 
         public void RemoveCoverArt () {
             FindCoverPhoto ();
 
             if (coverPhoto != null && db.ArtworkDatabase != null) {
                 db.ArtworkDatabase.RemovePhoto (coverPhoto);
+                record.RightSideArtworkId = 0;
             }
         }
         
Index: src/TrackDatabase.cs
===================================================================
--- src/TrackDatabase.cs	(revision 149271)
+++ src/TrackDatabase.cs	(working copy)
@@ -1320,6 +1320,9 @@
                 GaplessAlbumFlag = ToInt16 (body, 246) == 1;
             }
                 
+            if (db.Version >= 25 && body.Length >= 344) {
+                RightSideArtworkId = ToInt32(body, 340);
+            }
 
             details.Clear();
 
@@ -2719,6 +2722,75 @@
 
             try
             {
+                // Optimize for sparse artwork if supported
+                if (device.SupportsSparseArtwork && device.ArtworkFormats.Count > 0) {            
+                    ArtworkFormat comparisonFormat = null;
+                    
+                    Dictionary<int, List<Track>> artworkLookup = new Dictionary<int, List<Track>> ();
+
+                    foreach (Track track in tracks) {
+                        // The full size photo isn't set for covers, so pick
+                        // the first supported thumb format we find in use and compare with that
+                        // (Just picking device.ArtworkFormats[0] does not seem to work)
+                        if (comparisonFormat == null) {
+                            foreach (ArtworkFormat format in device.ArtworkFormats) {
+                                if (track.GetCoverArt(format) != null) {
+                                    comparisonFormat = format;
+                                    break;
+                                }
+                            }
+                        }
+                        
+                        if (comparisonFormat == null || track.GetCoverArt(comparisonFormat) == null)
+                            continue;
+                        
+                        int hash = Utility.Sum(track.GetCoverArt(comparisonFormat));
+                        bool matchFound = false;
+                        List<Track> possibleMatches = null;
+                        
+                        if (artworkLookup.TryGetValue(hash, out possibleMatches))
+                        {
+                            foreach (Track matchingTrack in possibleMatches)
+                            {
+                                // they are already the same
+                                if (track.Record.RightSideArtworkId == matchingTrack.Record.RightSideArtworkId)
+                                {
+                                    matchFound = true;
+                                    break;
+                                }
+                                
+                                // check to see if thumb and album name are the same
+                                if (Utility.Equals(track.GetCoverArt(comparisonFormat), matchingTrack.GetCoverArt(comparisonFormat)) &&
+                                    track.Album.Equals(matchingTrack.Album))
+                                {
+                                    track.ShareCoverArt(matchingTrack);
+                                    
+                                    matchFound = true;
+                                    break;
+                                }
+                            }
+                        } else {
+                            possibleMatches = new List<Track> ();
+                            artworkLookup.Add(hash, possibleMatches);
+                        }
+                        
+                        if (!matchFound)
+                            possibleMatches.Add(track);
+                    }
+                    
+                    // Prune unused covers
+                    Dictionary<int, bool> usedArtwork = new Dictionary<int, bool> ();
+    
+                    foreach (Track track in tracks) {
+                        usedArtwork[track.Record.RightSideArtworkId] = true;
+                    }
+                    
+                    foreach (Photo photo in artdb.Photos) {
+                        if (!usedArtwork.ContainsKey(photo.Id))
+                            artdb.RemovePhoto(photo);
+                    }
+                }
+                
                 // Save the tracks db
                 using (BinaryWriter writer = new EndianBinaryWriter(new FileStream(TrackDbPath, FileMode.Create),
                                                                     dbrec.IsBE))
@@ -2911,8 +2983,9 @@
                 dbrec[DataSetIndex.Library].TrackList.Remove(track.Record.Id);
                 dbrec[DataSetIndex.Playlist].Library.RemoveTrack(track.Record.Id);
 
-                // remove from cover art db
-                if (artdb != null)
+                // remove from cover art db if we aren't doing sparse artwork,
+                // otherwise it will be removed in Save()
+                if (artdb != null && (track.Record.RightSideArtworkId == 0 || !device.SupportsSparseArtwork))
                 {
                     Photo artPhoto = artdb.LookupPhotoByTrackId(track.Record.DatabaseId);
                     if (artPhoto != null)
