├── src
├── Clickwheel
│ ├── clickwheel.128.png
│ ├── Resources
│ │ └── ArtworkDB-empty
│ ├── Parsers
│ │ ├── iTunesDB
│ │ │ ├── MHOD
│ │ │ │ ├── StringMHOD.cs
│ │ │ │ ├── UnknownMHOD.cs
│ │ │ │ ├── PlaylistPositionMHOD.cs
│ │ │ │ ├── MHODFactory.cs
│ │ │ │ ├── UnicodeMHOD.cs
│ │ │ │ ├── BaseMHODElement.cs
│ │ │ │ ├── ArtworkStringMHOD.cs
│ │ │ │ └── MenuIndexMHOD.cs
│ │ │ ├── AlbumListContainer.cs
│ │ │ ├── UnknownListContainer.cs
│ │ │ ├── PlaylistListContainer.cs
│ │ │ ├── PlaylistListV2Container.cs
│ │ │ ├── TrackListContainer.cs
│ │ │ ├── DatabaseHash
│ │ │ │ ├── DatabaseHasher.cs
│ │ │ │ ├── Hash72.cs
│ │ │ │ └── HashInfo.cs
│ │ │ ├── ListContainerHeader.cs
│ │ │ ├── IdGenerator.cs
│ │ │ ├── PodcastListAdapter.cs
│ │ │ ├── iTunesDBRoot.cs
│ │ │ └── PlaylistItem.cs
│ │ ├── iTunesSD
│ │ │ ├── ITunesSD.cs
│ │ │ ├── Header.cs
│ │ │ └── Entry.cs
│ │ ├── Artwork
│ │ │ ├── ImageListContainer.cs
│ │ │ ├── IThmbFileListContainer.cs
│ │ │ ├── ImageAlbumListContainer.cs
│ │ │ ├── UnknownListContainer.cs
│ │ │ ├── ArtworkHelper.cs
│ │ │ ├── MHODType2.cs
│ │ │ ├── ImageAlbumItem.cs
│ │ │ ├── IThmbFile.cs
│ │ │ ├── ImageAlbumList.cs
│ │ │ ├── IThmbFileList.cs
│ │ │ ├── ListContainerHeader.cs
│ │ │ ├── PhotoDB.cs
│ │ │ ├── ArtworkDBRoot.cs
│ │ │ ├── ImageList.cs
│ │ │ └── ImageAlbum.cs
│ │ ├── PlayCounts
│ │ │ ├── Entry.cs
│ │ │ ├── Header.cs
│ │ │ └── PlayCounts.cs
│ │ ├── Base
│ │ │ ├── BaseDatabaseElement.cs
│ │ │ └── BaseDatabase.cs
│ │ ├── iTunesCDB
│ │ │ └── ITunesCDBRoot.cs
│ │ └── MusicDatabase.cs
│ ├── Exceptions
│ │ ├── UnsupportedArtworkFormatException.cs
│ │ ├── IPodNotFoundException.cs
│ │ ├── InvalidValueException.cs
│ │ ├── InvalidIPodDriveException.cs
│ │ ├── ExtendedSysInfoNotFoundException.cs
│ │ ├── OperationNotAllowedException.cs
│ │ ├── UnknownSortOrderException.cs
│ │ ├── UnsupportedIPodException.cs
│ │ ├── ArtworkDBNotFoundException.cs
│ │ ├── NoSupportedArtworkException.cs
│ │ ├── OutOfDiskSpaceException.cs
│ │ ├── ParseException.cs
│ │ ├── ITunesLockException.cs
│ │ ├── BaseClickwheelException.cs
│ │ ├── TrackAlreadyExistsException.cs
│ │ └── UnsupportedITunesVersionException.cs
│ ├── iPodFamilyEnum.cs
│ ├── DataTypes
│ │ ├── IPodTrackSize.cs
│ │ ├── IPodTrackLength.cs
│ │ ├── IPodDateTime.cs
│ │ └── IPodRating.cs
│ ├── Clickwheel.cs
│ ├── IPodDevice
│ │ └── FileSystems
│ │ │ ├── IPodDriveInfo.cs
│ │ │ └── IDeviceInfo.cs
│ ├── Session.cs
│ ├── Clickwheel.csproj
│ ├── NewTrack.cs
│ ├── packages.lock.json
│ ├── IPodBackup.cs
│ └── DebugLogger.cs
├── Clickwheel.DeviceHelper.GUI
│ ├── icon.ico
│ ├── icon.png
│ ├── NativeMethods.txt
│ ├── App.xaml.cs
│ ├── AssemblyInfo.cs
│ ├── README.md
│ ├── App.xaml
│ ├── Clickwheel.DeviceHelper.GUI.csproj
│ ├── MainWindow.xaml
│ ├── packages.lock.json
│ └── MainWindow.xaml.cs
└── Clickwheel.DeviceHelper
│ ├── NativeMethods.txt
│ ├── DeviceHelper.cs
│ ├── ScsiPassThroughWithBuffers.cs
│ ├── Clickwheel.DeviceHelper.csproj
│ └── packages.lock.json
├── tests
└── Clickwheel.Tests
│ ├── Fixtures
│ ├── HashInfo
│ ├── sample-image-red.png
│ ├── ipod-test-db-hashed.db
│ ├── sample-image-blue.png
│ └── sample-image-green.png
│ ├── TestConfig.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Parsers
│ ├── iTunesDB
│ │ └── DatabaseHash
│ │ │ ├── Hash58Test.cs
│ │ │ ├── HashInfoTest.cs
│ │ │ └── Hash72Test.cs
│ ├── Artwork
│ │ └── ArtworkHelperTest.cs
│ └── HelpersTest.cs
│ └── Clickwheel.Tests.csproj
├── .gitignore
├── clickwheel-logo.svg
├── clickwheel-wordmark-on-dark.svg
├── clickwheel-wordmark-on-light.svg
└── README.md
/src/Clickwheel/clickwheel.128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/src/Clickwheel/clickwheel.128.png
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper.GUI/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/src/Clickwheel.DeviceHelper.GUI/icon.ico
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper.GUI/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/src/Clickwheel.DeviceHelper.GUI/icon.png
--------------------------------------------------------------------------------
/src/Clickwheel/Resources/ArtworkDB-empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/src/Clickwheel/Resources/ArtworkDB-empty
--------------------------------------------------------------------------------
/tests/Clickwheel.Tests/Fixtures/HashInfo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/tests/Clickwheel.Tests/Fixtures/HashInfo
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper.GUI/NativeMethods.txt:
--------------------------------------------------------------------------------
1 | DwmSetWindowAttribute
2 | SHGetStockIconInfo
3 | DestroyIcon
4 | SHSTOCKICONINFO
5 | SHGSI_FLAGS
--------------------------------------------------------------------------------
/tests/Clickwheel.Tests/Fixtures/sample-image-red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/tests/Clickwheel.Tests/Fixtures/sample-image-red.png
--------------------------------------------------------------------------------
/tests/Clickwheel.Tests/Fixtures/ipod-test-db-hashed.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/tests/Clickwheel.Tests/Fixtures/ipod-test-db-hashed.db
--------------------------------------------------------------------------------
/tests/Clickwheel.Tests/Fixtures/sample-image-blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/tests/Clickwheel.Tests/Fixtures/sample-image-blue.png
--------------------------------------------------------------------------------
/tests/Clickwheel.Tests/Fixtures/sample-image-green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dstaley/clickwheel/HEAD/tests/Clickwheel.Tests/Fixtures/sample-image-green.png
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/iTunesDB/MHOD/StringMHOD.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Parsers.iTunesDB
2 | {
3 | abstract class StringMHOD : BaseMHODElement
4 | {
5 | public abstract string Data { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper/NativeMethods.txt:
--------------------------------------------------------------------------------
1 | CreateFile
2 | DeviceIoControl
3 | SCSI_PASS_THROUGH
4 | _SCSI_PASS_THROUGH
5 | SCSI_PASS_THROUGH_WITH_BUFFERS
6 | STORAGE_DEVICE_NUMBER
7 | IOCTL_SCSI_PASS_THROUGH
8 | IOCTL_STORAGE_GET_DEVICE_NUMBER
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper/DeviceHelper.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.DeviceHelper;
2 |
3 | public static class DeviceHelper
4 | {
5 | public static string GetExtendedSysInfoFromDrive(string driveLetter)
6 | {
7 | return DeviceXml.Get(driveLetter);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper.GUI/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace Clickwheel.DeviceHelper.GUI
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/UnsupportedArtworkFormatException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | public class UnsupportedArtworkFormatException : BaseClickwheelException
4 | {
5 | public UnsupportedArtworkFormatException(uint imageSize)
6 | : base($"The artwork format (size {imageSize}) is not currently supported.")
7 | {
8 | Category = "Unsupported Artwork format.";
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/IPodNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown when an iPod couldn't be found during a call to GetConnectedIPod()
5 | ///
6 | public class IPodNotFoundException : BaseClickwheelException
7 | {
8 | public IPodNotFoundException(string message) : base(message)
9 | {
10 | Category = "iPod could not be found";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/InvalidValueException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown when an invalid value is specified for a track or playlist property
5 | ///
6 | public class InvalidValueException : BaseClickwheelException
7 | {
8 | public InvalidValueException(string message) : base(message)
9 | {
10 | Category = "Invalid Value Specified";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/InvalidIPodDriveException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown when an invalid drive is specified to a call to IPod.GetIPodByDrive()
5 | ///
6 | public class InvalidIPodDriveException : BaseClickwheelException
7 | {
8 | public InvalidIPodDriveException(string message) : base(message)
9 | {
10 | Category = "iPod Not Found";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper/ScsiPassThroughWithBuffers.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using Windows.Win32.Storage.IscsiDisc;
3 |
4 | namespace Clickwheel.DeviceHelper
5 | {
6 | [StructLayout(LayoutKind.Sequential)]
7 | internal unsafe struct ScsiPassThroughWithBuffers
8 | {
9 | public SCSI_PASS_THROUGH spt;
10 | public uint Filler;
11 | public fixed byte ucSenseBuf[32];
12 | public fixed byte ucDataBuf[255];
13 | }
14 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build results
2 | [Dd]ebug/
3 | [Dd]ebugPublic/
4 | [Rr]elease/
5 | [Rr]eleases/
6 | x64/
7 | x86/
8 | [Ww][Ii][Nn]32/
9 | [Aa][Rr][Mm]/
10 | [Aa][Rr][Mm]64/
11 | bld/
12 | [Bb]in/
13 | [Oo]bj/
14 | [Ll]og/
15 | [Ll]ogs/
16 |
17 | # Visual Studio 2015/2017 cache/options directory
18 | .vs/
19 |
20 | # MSTest test Results
21 | [Tt]est[Rr]esult*/
22 | [Bb]uild[Ll]og.*
23 |
24 | *.DotSettings.user
25 |
26 | .DS_Store
27 |
28 | # Snapshot test files
29 | *.received.*
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/ExtendedSysInfoNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown if ExtendedSysInfo was not found
5 | ///
6 | public class ExtendedSysInfoNotFoundException : BaseClickwheelException
7 | {
8 | public ExtendedSysInfoNotFoundException() : base("ExtendedSysInfo file not found on iPod.")
9 | {
10 | Category = "ExtendedSysInfo not found";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/OperationNotAllowedException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown when (for example) trying to add/remove tracks from a Smart Playlist.
5 | ///
6 | public class OperationNotAllowedException : BaseClickwheelException
7 | {
8 | public OperationNotAllowedException(string message) : base(message)
9 | {
10 | Category = "Operation not allowed";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/UnknownSortOrderException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown if a Playlist's SortOrder field is not a value enumerated by Clickwheel.
5 | ///
6 | public class UnknownSortOrderException : BaseClickwheelException
7 | {
8 | public UnknownSortOrderException(string message) : base(message)
9 | {
10 | Category = "The playlist's Sort Order value is not supported";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper.GUI/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/UnsupportedIPodException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown when the iPod is not supported. This can be achieved by setting iPod.IsWritable=false.
5 | ///
6 | ///
7 | public class UnsupportedIPodException : BaseClickwheelException
8 | {
9 | public UnsupportedIPodException(string message) : base(message)
10 | {
11 | Category = "iPod not fully supported";
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/ArtworkDBNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown if artwork is added to an iPod without an ArtworkDB
5 | ///
6 | public class ArtworkDBNotFoundException : BaseClickwheelException
7 | {
8 | public ArtworkDBNotFoundException()
9 | : base("iPod ArtworkDB not found. You cannot add or remove artwork.")
10 | {
11 | Category = "Artwork Problem";
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/NoSupportedArtworkException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown when trying to add album artwork and the iPod reported no supported artwork.
5 | ///
6 | public class NoSupportedArtworkException : BaseClickwheelException
7 | {
8 | public NoSupportedArtworkException() : base("No supported artwork formats were detected")
9 | {
10 | Category = "Album art could not be added";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tests/Clickwheel.Tests/TestConfig.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Runtime.CompilerServices;
3 | using VerifyTests;
4 |
5 | namespace Clickwheel.Tests
6 | {
7 | public static class TestConfig
8 | {
9 | [ModuleInitializer]
10 | public static void Init()
11 | {
12 | VerifierSettings.DerivePathInfo(
13 | (sourceFile, projectDirectory, type, method) =>
14 | new(Path.Combine(projectDirectory, "Fixtures", "Snapshots"), type.Name, method.Name)
15 | );
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/OutOfDiskSpaceException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown when adding tracks and adding new artwork to the iPod. Clickwheel will make sure there will be at least 10Mb of free space on the
5 | /// iPod after copying the track.
6 | ///
7 | public class OutOfDiskSpaceException : BaseClickwheelException
8 | {
9 | public OutOfDiskSpaceException(string message) : base(message)
10 | {
11 | Category = "The iPod cannot store any more files";
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper.GUI/README.md:
--------------------------------------------------------------------------------
1 | # Clickwheel Device Helper
2 |
3 | Clickwheel Device Helper is a Windows application that automatically writes the `SysInfoExtended` file to all connected iPods.
4 |
5 | ## Installation
6 |
7 | Clickwheel Device Helper requires the installation of the [.NET Desktop Runtime 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0).
8 |
9 | [Download Clickwheel Device Helper](https://github.com/dstaley/clickwheel/releases/tag/clickwheel-device-helper-v1.0.0)
10 |
11 | ## Usage
12 |
13 | Run `Clickwheel Device Helper.exe` with your iPod connected to your computer, and then click "Start".
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper/Clickwheel.DeviceHelper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0-windows
5 | enable
6 | enable
7 | true
8 |
9 |
10 |
11 |
12 | all
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/ParseException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Clickwheel.Exceptions
4 | {
5 | ///
6 | /// Thrown when the iPod database format could not be recognized or validated.
7 | /// This could occur if the iTunes file format changes or a 3rd party application has written the
8 | /// database in a different way to iTunes.
9 | ///
10 | public class ParseException : BaseClickwheelException
11 | {
12 | public ParseException(string message, Exception innerException)
13 | : base(message, innerException)
14 | {
15 | Category = "Your iPod database could not be read";
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/Clickwheel.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("Clickwheel.Tests")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("Clickwheel.Tests")]
9 | [assembly: AssemblyCopyright("Copyright © 2022")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 |
13 | [assembly: ComVisible(false)]
14 |
15 | [assembly: Guid("6f8de93f-68e3-45cf-99ee-f8d7c4c72625")]
16 |
17 | // [assembly: AssemblyVersion("1.0.*")]
18 | [assembly: AssemblyVersion("1.0.0.0")]
19 | [assembly: AssemblyFileVersion("1.0.0.0")]
20 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/ITunesLockException.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel.Exceptions
2 | {
3 | ///
4 | /// Thrown when an iTunesLock file exists on the iPod. This usually means iTunes has locked the iPod
5 | /// and is currently syncing.
6 | ///
7 | public class ITunesLockException : BaseClickwheelException
8 | {
9 | public ITunesLockException(string lockFilePath)
10 | : base(
11 | $"iTunes has locked the iPod database. Please wait for iTunes to finish synchronizing. \r\nIf iTunes is not running, delete the '{lockFilePath}' file."
12 | )
13 | {
14 | Category = "iPod cannot be opened";
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Clickwheel/iPodFamilyEnum.cs:
--------------------------------------------------------------------------------
1 | namespace Clickwheel
2 | {
3 | ///
4 | /// Enumeration of the different iPod types
5 | ///
6 | public enum IPodFamily
7 | {
8 | Unknown = 0,
9 | iPod_Gen1_Gen2 = 1,
10 | iPod_Gen3 = 2,
11 | iPod_Mini = 3,
12 | iPod_Gen4 = 4,
13 | iPod_Gen4_2 = 5,
14 | iPod_Gen5 = 6,
15 | iPod_Nano_Gen1 = 7,
16 | iPod_Nano_Gen2 = 9,
17 | iPod_Classic = 11,
18 | iPod_Nano_Gen3 = 12,
19 | iPod_Nano_Gen4 = 15,
20 | iPod_Nano_Gen5 = 16,
21 | iPod_Shuffle_Gen1 = 128,
22 | iPod_Shuffle_Gen2 = 130,
23 | iPod_Shuffle_Gen3 = 132,
24 | iPod_Shuffle_Gen4 = 133
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/BaseClickwheelException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Clickwheel.Exceptions
4 | {
5 | public class BaseClickwheelException : Exception
6 | {
7 | private string _category;
8 |
9 | public BaseClickwheelException(string message) : base(message)
10 | {
11 | _category = "Clickwheel Exception";
12 | }
13 |
14 | public BaseClickwheelException(string message, Exception innerException)
15 | : base(message, innerException)
16 | {
17 | _category = "Clickwheel Exception";
18 | }
19 |
20 | public string Category
21 | {
22 | get => _category;
23 | set => _category = value;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/iTunesDB/MHOD/UnknownMHOD.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.iTunesDB
4 | {
5 | class UnknownMHOD : BaseMHODElement
6 | {
7 | private byte[] _byteData;
8 |
9 | internal override void Read(IPod iPod, BinaryReader reader)
10 | {
11 | _byteData = reader.ReadBytes(_sectionSize - _headerSize);
12 | }
13 |
14 | internal override void Write(BinaryWriter writer)
15 | {
16 | base.Write(writer);
17 | writer.Write(_byteData);
18 | }
19 |
20 | internal override int GetSectionSize()
21 | {
22 | var size = _headerSize + _byteData.Length;
23 |
24 | return size;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Clickwheel.DeviceHelper.GUI/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/TrackAlreadyExistsException.cs:
--------------------------------------------------------------------------------
1 | using Clickwheel.Parsers.iTunesDB;
2 |
3 | namespace Clickwheel.Exceptions
4 | {
5 | ///
6 | /// Thrown when trying to add a track to the iPod which already exists. (Same Title, Artist, Album, TrackNumber)
7 | ///
8 | public class TrackAlreadyExistsException : BaseClickwheelException
9 | {
10 | private Track _existingTrack;
11 |
12 | public TrackAlreadyExistsException(string message, Track existingTrack) : base(message)
13 | {
14 | Category = "This track already exists on your iPod";
15 | _existingTrack = existingTrack;
16 | }
17 |
18 | ///
19 | /// Track that is on the iPod already
20 | ///
21 | public Track ExistingTrack => _existingTrack;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Clickwheel/Exceptions/UnsupportedITunesVersionException.cs:
--------------------------------------------------------------------------------
1 | using Clickwheel.Parsers;
2 |
3 | namespace Clickwheel.Exceptions
4 | {
5 | ///
6 | /// Thrown when the iPod database version is below 0x14. iTunes 7.1 and above create 0x14(+) databases.
7 | /// If the version is below 0x14, Clickwheel will try and read it, but will not enable modifications.
8 | ///
9 | public class UnsupportedITunesVersionException : BaseClickwheelException
10 | {
11 | private CompatibilityType _compatibility;
12 |
13 | public UnsupportedITunesVersionException(string message, CompatibilityType compatibility)
14 | : base(message)
15 | {
16 | Category = "iPod database not supported";
17 | _compatibility = compatibility;
18 | }
19 |
20 | public CompatibilityType Compatibility => _compatibility;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Clickwheel.Tests/Parsers/iTunesDB/DatabaseHash/Hash58Test.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using Clickwheel.DatabaseHash;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 |
6 | namespace Clickwheel.Tests.Parsers.iTunesDB.DatabaseHash
7 | {
8 | [TestClass]
9 | public class Hash58Test
10 | {
11 | [TestMethod]
12 | [DeploymentItem(@"Fixtures/ipod-test-db-hashed.db")]
13 | public void TestHash58()
14 | {
15 | byte[] db = File.ReadAllBytes("ipod-test-db-hashed.db");
16 | byte[] result = Hash58.GenerateDatabaseHash("000A27001A26973B", db);
17 | StringBuilder sb = new StringBuilder();
18 | foreach (byte b in result)
19 | {
20 | sb.AppendFormat("{0:x}", b);
21 | }
22 | Assert.AreEqual("9134cb64dfa38c16dbcfb023768ddcaf8fbe34a2", sb.ToString());
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Clickwheel/DataTypes/IPodTrackSize.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Clickwheel.Parsers;
3 |
4 | namespace Clickwheel.DataTypes
5 | {
6 | ///
7 | /// Wraps a file size in bytes and a human-readable string describing the size.
8 | ///
9 | public class IPodTrackSize : IComparable
10 | {
11 | uint _trackSize;
12 | string _trackSizeMB;
13 |
14 | public IPodTrackSize(uint trackSizeInBytes)
15 | {
16 | _trackSize = trackSizeInBytes;
17 | _trackSizeMB = Helpers.GetFileSizeString(trackSizeInBytes, 1);
18 | }
19 |
20 | public uint ByteCount => _trackSize;
21 |
22 | public override string ToString()
23 | {
24 | return _trackSizeMB;
25 | }
26 |
27 | #region IComparable Members
28 |
29 | public int CompareTo(object obj)
30 | {
31 | return _trackSize.CompareTo(((IPodTrackSize)obj).ByteCount);
32 | }
33 |
34 | #endregion
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/iTunesSD/ITunesSD.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.iTunesSD
4 | {
5 | class ITunesSD
6 | {
7 | IPod _iPod;
8 | Header _header;
9 |
10 | public ITunesSD(IPod iPod)
11 | {
12 | _iPod = iPod;
13 | _header = new Header(iPod);
14 | }
15 |
16 | public void Backup()
17 | {
18 | var iTunesSDPath = _iPod.FileSystem.ITunesSDPath;
19 | if (_iPod.FileSystem.FileExists(iTunesSDPath))
20 | {
21 | File.Copy(iTunesSDPath, iTunesSDPath + ".spbackup", true);
22 | }
23 | }
24 |
25 | public void Generate()
26 | {
27 | var iTunesSDPath = _iPod.FileSystem.ITunesSDPath;
28 | var fs = new FileStream(iTunesSDPath, FileMode.Create, FileAccess.Write);
29 | var writer = new BinaryWriter(fs);
30 |
31 | _header.Write(writer);
32 | writer.Flush();
33 | writer.Close();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/iTunesDB/AlbumListContainer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.iTunesDB
4 | {
5 | ///
6 | /// Implements a type 4 (Album list) MHSD entry in iTunesDB
7 | ///
8 | class AlbumListContainer : BaseDatabaseElement
9 | {
10 | private ListContainerHeader _header;
11 | private byte[] _unk1;
12 |
13 | public AlbumListContainer(ListContainerHeader parent)
14 | {
15 | _header = parent;
16 | }
17 |
18 | internal override void Read(IPod iPod, BinaryReader reader)
19 | {
20 | base.Read(iPod, reader);
21 | var length = _header.SectionSize - _header.HeaderSize;
22 | _unk1 = reader.ReadBytes(length);
23 | }
24 |
25 | internal override void Write(BinaryWriter writer)
26 | {
27 | writer.Write(_unk1);
28 | }
29 |
30 | internal override int GetSectionSize()
31 | {
32 | return _header.SectionSize;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/Artwork/ImageListContainer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.Artwork
4 | {
5 | ///
6 | /// Implements a type 1 (Image list) MHSD entry in ArtworkDB
7 | ///
8 | class ImageListContainer : BaseDatabaseElement
9 | {
10 | private ListContainerHeader _header;
11 | ImageList _childSection;
12 |
13 | public ImageListContainer(ListContainerHeader parent)
14 | {
15 | _header = parent;
16 | }
17 |
18 | internal override void Read(IPod iPod, BinaryReader reader)
19 | {
20 | base.Read(iPod, reader);
21 | _childSection = new ImageList();
22 | _childSection.Read(iPod, reader);
23 | }
24 |
25 | internal override void Write(BinaryWriter writer)
26 | {
27 | _childSection.Write(writer);
28 | }
29 |
30 | internal override int GetSectionSize()
31 | {
32 | return _header.HeaderSize + _childSection.GetSectionSize();
33 | }
34 |
35 | internal ImageList ImageList => _childSection;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/Artwork/IThmbFileListContainer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.Artwork
4 | {
5 | ///
6 | /// Implements a type 3 (file list) MHSD entry in ArtworkDB
7 | ///
8 | class IThmbFileListContainer : BaseDatabaseElement
9 | {
10 | private ListContainerHeader _header;
11 | IThmbFileList _childSection;
12 |
13 | public IThmbFileListContainer(ListContainerHeader parent)
14 | {
15 | _header = parent;
16 | }
17 |
18 | internal override void Read(IPod iPod, BinaryReader reader)
19 | {
20 | base.Read(iPod, reader);
21 | _childSection = new IThmbFileList();
22 | _childSection.Read(iPod, reader);
23 | }
24 |
25 | internal override void Write(BinaryWriter writer)
26 | {
27 | _childSection.Write(writer);
28 | }
29 |
30 | internal override int GetSectionSize()
31 | {
32 | return _header.HeaderSize + _childSection.GetSectionSize();
33 | }
34 |
35 | internal IThmbFileList FileList => _childSection;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/Artwork/ImageAlbumListContainer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.Artwork
4 | {
5 | ///
6 | /// Implements a type 2 (Image album list) MHSD entry in ArtworkDB / PhotoDB
7 | ///
8 | class ImageAlbumListContainer : BaseDatabaseElement
9 | {
10 | private ListContainerHeader _header;
11 | ImageAlbumList _childSection;
12 |
13 | public ImageAlbumListContainer(ListContainerHeader parent)
14 | {
15 | _header = parent;
16 | }
17 |
18 | internal override void Read(IPod iPod, BinaryReader reader)
19 | {
20 | base.Read(iPod, reader);
21 | _childSection = new ImageAlbumList();
22 | _childSection.Read(iPod, reader);
23 | }
24 |
25 | internal override void Write(BinaryWriter writer)
26 | {
27 | _childSection.Write(writer);
28 | }
29 |
30 | internal override int GetSectionSize()
31 | {
32 | return _header.HeaderSize + _childSection.GetSectionSize();
33 | }
34 |
35 | internal ImageAlbumList ImageAlbumList => _childSection;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/Artwork/UnknownListContainer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.Artwork
4 | {
5 | ///
6 | /// Implements any unknown type MHSD entry in iTunesDB
7 | /// Simply reads to the end of the list, ignoring the contents.
8 | /// Role is to protect against future iTunesDB changes.
9 | ///
10 | class UnknownListContainer : BaseDatabaseElement
11 | {
12 | private ListContainerHeader _header;
13 | private byte[] _unk1;
14 |
15 | public UnknownListContainer(ListContainerHeader parent)
16 | {
17 | _header = parent;
18 | }
19 |
20 | internal override void Read(IPod iPod, BinaryReader reader)
21 | {
22 | base.Read(iPod, reader);
23 | var length = _header.SectionSize - _header.HeaderSize;
24 | _unk1 = reader.ReadBytes(length);
25 | }
26 |
27 | internal override void Write(BinaryWriter writer)
28 | {
29 | writer.Write(_unk1);
30 | }
31 |
32 | internal override int GetSectionSize()
33 | {
34 | return _header.SectionSize;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/iTunesDB/UnknownListContainer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.iTunesDB
4 | {
5 | ///
6 | /// Implements any unknown type MHSD entry in iTunesDB
7 | /// Simply reads to the end of the list, ignoring the contents.
8 | /// Role is to protect against future iTunesDB changes.
9 | ///
10 | class UnknownListContainer : BaseDatabaseElement
11 | {
12 | private ListContainerHeader _header;
13 | private byte[] _unk1;
14 |
15 | public UnknownListContainer(ListContainerHeader parent)
16 | {
17 | _header = parent;
18 | }
19 |
20 | internal override void Read(IPod iPod, BinaryReader reader)
21 | {
22 | base.Read(iPod, reader);
23 | var length = _header.SectionSize - _header.HeaderSize;
24 | _unk1 = reader.ReadBytes(length);
25 | }
26 |
27 | internal override void Write(BinaryWriter writer)
28 | {
29 | writer.Write(_unk1);
30 | }
31 |
32 | internal override int GetSectionSize()
33 | {
34 | return _header.SectionSize;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Clickwheel/Clickwheel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using Clickwheel.IPodDevice.FileSystems;
4 |
5 | namespace Clickwheel
6 | {
7 | ///
8 | /// Contains Clickwheel-specific methods.
9 | ///
10 | public static class Clickwheel
11 | {
12 | private static List _registeredFileSystems = new List();
13 |
14 | static Clickwheel()
15 | {
16 | var iPodProfile = new StandardFileSystem(
17 | "IPod",
18 | Path.Combine("iPod_Control", "iTunes"),
19 | @"iPod_Control",
20 | Path.Combine("iPod_Control", "Artwork"),
21 | @"Photos"
22 | );
23 | iPodProfile.ParseDbFilesLocally = true;
24 | _registeredFileSystems.Add(iPodProfile);
25 | }
26 |
27 | ///
28 | /// List of device FileSystems Clickwheel will use when searching for iPods. This list can be updated
29 | /// dynamically before calling GetConnectediPod().
30 | ///
31 | public static List RegisteredFileSystems => _registeredFileSystems;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Clickwheel/DataTypes/IPodTrackLength.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Clickwheel.Parsers;
3 |
4 | namespace Clickwheel.DataTypes
5 | {
6 | ///
7 | /// Wraps a track length in milliseconds and a human-readable hh:mm:ss string
8 | ///
9 | public class IPodTrackLength : IComparable
10 | {
11 | uint _trackLengthMSecs;
12 | string _trackLengthMinsSecs;
13 |
14 | public IPodTrackLength(uint trackLengthInMSecs)
15 | {
16 | _trackLengthMSecs = trackLengthInMSecs;
17 | _trackLengthMinsSecs = Helpers.GetTimeString(this.Seconds);
18 | }
19 |
20 | public IPodTrackLength(int trackLengthInMSecs) : this((uint)trackLengthInMSecs) { }
21 |
22 | public uint Seconds => _trackLengthMSecs / 1000;
23 |
24 | public uint MilliSeconds => _trackLengthMSecs;
25 |
26 | public override string ToString()
27 | {
28 | return _trackLengthMinsSecs;
29 | }
30 |
31 | #region IComparable Members
32 |
33 | public int CompareTo(object obj)
34 | {
35 | return _trackLengthMSecs.CompareTo(((IPodTrackLength)obj).MilliSeconds);
36 | }
37 |
38 | #endregion
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/iTunesDB/PlaylistListContainer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.iTunesDB
4 | {
5 | ///
6 | /// Implements a type 2 (Playlists list) MHSD entry in iTunesDB
7 | ///
8 | class PlaylistListContainer : BaseDatabaseElement
9 | {
10 | private ListContainerHeader _header;
11 | private PlaylistList _childSection;
12 |
13 | internal PlaylistListContainer(ListContainerHeader parent)
14 | {
15 | _header = parent;
16 | }
17 |
18 | internal override void Read(IPod iPod, BinaryReader reader)
19 | {
20 | base.Read(iPod, reader);
21 | _childSection = new PlaylistList();
22 | _childSection.Read(iPod, reader);
23 | }
24 |
25 | internal override void Write(BinaryWriter writer)
26 | {
27 | _childSection.Write(writer);
28 | }
29 |
30 | internal override int GetSectionSize()
31 | {
32 | return _header.HeaderSize + _childSection.GetSectionSize();
33 | }
34 |
35 | internal PlaylistList GetPlaylistsList()
36 | {
37 | return _childSection;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/iTunesDB/PlaylistListV2Container.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.iTunesDB
4 | {
5 | ///
6 | /// Implements a type 3 (Playlist v2 list) MHSD entry in iTunesDB
7 | ///
8 | class PlaylistListV2Container : BaseDatabaseElement
9 | {
10 | private ListContainerHeader _header;
11 | private PlaylistList _childSection;
12 |
13 | public PlaylistListV2Container(ListContainerHeader parent)
14 | {
15 | _header = parent;
16 | }
17 |
18 | internal override void Read(IPod iPod, BinaryReader reader)
19 | {
20 | base.Read(iPod, reader);
21 | _childSection = new PlaylistList();
22 | _childSection.Read(iPod, reader);
23 | }
24 |
25 | internal override void Write(BinaryWriter writer)
26 | {
27 | _childSection.Write(writer);
28 | }
29 |
30 | internal override int GetSectionSize()
31 | {
32 | return _header.HeaderSize + _childSection.GetSectionSize();
33 | }
34 |
35 | internal PlaylistList GetPlaylistsList()
36 | {
37 | return _childSection;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Clickwheel/IPodDevice/FileSystems/IPodDriveInfo.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.IPodDevice.FileSystems
4 | {
5 | public class IPodDriveInfo
6 | {
7 | private DriveInfo _driveInfo;
8 | private DirectoryInfo _directoryInfo;
9 |
10 | public IPodDriveInfo(DriveInfo driveInfo)
11 | {
12 | _driveInfo = driveInfo;
13 | }
14 |
15 | public IPodDriveInfo(DirectoryInfo directoryInfo)
16 | {
17 | _directoryInfo = directoryInfo;
18 | }
19 |
20 | public IPodDriveInfo(string drivePath)
21 | {
22 | _driveInfo = new DriveInfo(drivePath);
23 | }
24 |
25 | public bool IsReady => _driveInfo?.IsReady ?? true;
26 |
27 | public DriveType DriveType => _driveInfo?.DriveType ?? DriveType.Fixed;
28 |
29 | public string Name => _driveInfo?.Name ?? _directoryInfo.FullName;
30 |
31 | public long TotalSize =>
32 | _driveInfo?.TotalSize ?? new DriveInfo(_directoryInfo.Root.FullName).TotalSize;
33 |
34 | public long AvailableFreeSpace =>
35 | _driveInfo?.AvailableFreeSpace
36 | ?? new DriveInfo(_directoryInfo.Root.FullName).AvailableFreeSpace;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Clickwheel/Parsers/iTunesDB/TrackListContainer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Clickwheel.Parsers.iTunesDB
4 | {
5 | ///
6 | /// Implements a type 1 (Tracks list) MHSD entry in iTunesDB
7 | ///
8 | class TrackListContainer : BaseDatabaseElement
9 | {
10 | private ListContainerHeader _header;
11 | TrackList _childSection;
12 |
13 | public TrackListContainer(ListContainerHeader parent)
14 | {
15 | _header = parent;
16 | }
17 |
18 | #region IDatabaseElement Members
19 |
20 | internal override void Read(IPod iPod, BinaryReader reader)
21 | {
22 | base.Read(iPod, reader);
23 | _childSection = new TrackList();
24 | _childSection.Read(iPod, reader);
25 | }
26 |
27 | internal override void Write(BinaryWriter writer)
28 | {
29 | _childSection.Write(writer);
30 | }
31 |
32 | internal override int GetSectionSize()
33 | {
34 | return _header.HeaderSize + _childSection.GetSectionSize();
35 | }
36 |
37 | #endregion
38 |
39 | internal TrackList GetTrackList()
40 | {
41 | return _childSection;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Clickwheel/Session.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using Clickwheel.Parsers.iTunesDB;
4 |
5 | namespace Clickwheel
6 | {
7 | class Session
8 | {
9 | IPod _iPod;
10 | public List