diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/Internal/Linux/Methods.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/Internal/Linux/Methods.cs new file mode 100644 index 00000000..7667a423 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/Internal/Linux/Methods.cs @@ -0,0 +1,33 @@ +// +// Methods.cs +// +// Author: +// Mike Becker +// +// Copyright (c) 2019 Mike Becker +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +using System.Runtime.InteropServices; + +namespace UniversalEditor.Accessors.MTP.Internal.Linux +{ + public class Methods + { + public const string LIBRARY_FILENAME = "gio"; + + [DllImport(LIBRARY_FILENAME)] + public static extern IntPtr g_file_new_for_uri(string uri); + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/AbstractTrackList.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/AbstractTrackList.cs new file mode 100644 index 00000000..21cee0f1 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/AbstractTrackList.cs @@ -0,0 +1,149 @@ +/*************************************************************************** + * AbstractTrackList.cs + * + * Copyright (C) 2008 Novell + * Copyright (C) 2010 Alan McGovern + * Authors: + * Gabriel Burt (gburt@novell.com) + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public abstract class AbstractTrackList + { + private bool saved; + private List track_ids; + private MtpDevice device; + + public abstract uint Count { get; protected set; } + public abstract string Name { get; set; } + protected abstract IntPtr TracksPtr { get; set; } + + protected abstract int Create(); + protected abstract int Update(); + + public bool Saved { get { return saved; } } + protected MtpDevice Device { get { return device; } } + + public IList TrackIds + { + get { return track_ids; } + } + + protected AbstractTrackList(MtpDevice device) + { + if (device == null) + { + throw new ArgumentNullException("device"); + } + + this.device = device; + + if (track_ids == null) + { + track_ids = new List(); + } + } + + internal AbstractTrackList(MtpDevice device, IntPtr tracks, uint count) : this(device) + { + this.saved = true; + this.track_ids = new List(); + + if (tracks != IntPtr.Zero) + { + for (int i = 0; i < (int)count; i++) + track_ids.Add((uint)Marshal.ReadInt32(tracks, sizeof(int) * i)); + } + } + + public void AddTrack(Track track) + { + AddTrack(track.FileId); + } + + public void AddTrack(uint track_id) + { + track_ids.Add(track_id); + Count++; + } + + public void RemoveTrack(Track track) + { + RemoveTrack(track.FileId); + } + + public void RemoveTrack(uint track_id) + { + track_ids.Remove(track_id); + Count--; + } + + public void ClearTracks() + { + track_ids.Clear(); + Count = 0; + } + + public virtual void Save() + { + Count = (uint)track_ids.Count; + + if (TracksPtr != IntPtr.Zero) + throw new InvalidOperationException("TracksPtr must be NULL when Save is called"); + + try + { + if (Count > 0) + { + TracksPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)) * (int)Count); + for (int i = 0; i < track_ids.Count; i++) + Marshal.WriteInt32(TracksPtr, i * sizeof(int), (int)track_ids[i]); + } + + if (saved) + { + saved = Update() == 0; + } + else + { + saved = Create() == 0; + } + } + finally + { + if (TracksPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(TracksPtr); + TracksPtr = IntPtr.Zero; + } + } + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Album.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Album.cs new file mode 100644 index 00000000..f1e77e25 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Album.cs @@ -0,0 +1,234 @@ +/*************************************************************************** + * Album.cs + * + * Copyright (C) 2008 Novell + * Authors: + * Gabriel Burt (gburt@novell.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public class Album : AbstractTrackList + { + internal static List GetAlbums(MtpDevice device) + { + List albums = new List(); + + IntPtr ptr = LIBMTP_Get_Album_List(device.Handle); + while (ptr != IntPtr.Zero) + { + // Destroy the struct *after* we use it to ensure we don't access freed memory + // for the 'tracks' variable + AlbumStruct d = (AlbumStruct)Marshal.PtrToStructure(ptr, typeof(AlbumStruct)); + albums.Add(new Album(device, d)); + LIBMTP_destroy_album_t(ptr); + ptr = d.next; + } + + return albums; + } + + private AlbumStruct album; + + public uint AlbumId + { + get { return Saved ? album.album_id : 0; } + } + + public override string Name + { + get { return album.name; } + set { album.name = value; } + } + + public string Artist + { + get { return album.artist; } + set { album.artist = value; } + } + + public string Genre + { + get { return album.genre; } + set { album.genre = value; } + } + + public string Composer + { + get + { + return album.composer; + } + set + { + album.composer = value; + } + } + + public override uint Count + { + get { return album.no_tracks; } + protected set { album.no_tracks = value; } + } + + protected override IntPtr TracksPtr + { + get { return album.tracks; } + set { album.tracks = value; } + } + + public Album(MtpDevice device, string name, string artist, string genre, string composer) : base(device) + { + Name = name; + Artist = artist; + Genre = genre; + Composer = composer; + Count = 0; + } + + internal Album(MtpDevice device, AlbumStruct album) : base(device, album.tracks, album.no_tracks) + { + // Once we've loaded the tracks, set the TracksPtr to NULL as it + // will be freed when the Album constructor is finished. + this.album = album; + TracksPtr = IntPtr.Zero; + } + + public override void Save() + { + Save(null, 0, 0); + } + + public void Save(byte[] cover_art, uint width, uint height) + { + base.Save(); + if (Saved) + { + if (cover_art == null) + { + return; + } + + FileSampleData cover = new FileSampleData(); + cover.data = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte)) * cover_art.Length); + Marshal.Copy(cover_art, 0, cover.data, cover_art.Length); + cover.size = (ulong)cover_art.Length; + cover.width = width; + cover.height = height; + cover.filetype = FileType.JPEG; + + if (FileSample.LIBMTP_Send_Representative_Sample(Device.Handle, AlbumId, ref cover) != 0) + { + //Console.WriteLine ("Failed to send representative sample file for album {0} (id {1})", Name, AlbumId); + } + Marshal.FreeHGlobal(cover.data); + } + } + + protected override int Create() + { + return LIBMTP_Create_New_Album(Device.Handle, ref album); + } + + protected override int Update() + { + return LIBMTP_Update_Album(Device.Handle, ref album); + } + + public void Remove() + { + MtpDevice.LIBMTP_Delete_Object(Device.Handle, AlbumId); + } + + public override string ToString() + { + return String.Format("Album < Id: {4}, '{0}' by '{1}', genre '{2}', tracks {3} >", Name, Artist, Genre, Count, AlbumId); + } + + public static Album GetById(MtpDevice device, uint id) + { + IntPtr ptr = Album.LIBMTP_Get_Album(device.Handle, id); + if (ptr == IntPtr.Zero) + { + return null; + } + else + { + // Destroy the struct after we use it to prevent accessing freed memory + // in the 'tracks' variable + AlbumStruct album = (AlbumStruct)Marshal.PtrToStructure(ptr, typeof(AlbumStruct)); + var ret = new Album(device, album); + LIBMTP_destroy_album_t(ptr); + return ret; + } + } + + //[DllImport (MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //internal static extern IntPtr LIBMTP_new_album_t (); // LIBMTP_album_t* + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + static extern void LIBMTP_destroy_album_t(IntPtr album); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr LIBMTP_Get_Album_List(MtpDeviceHandle handle); // LIBMTP_album_t* + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr LIBMTP_Get_Album(MtpDeviceHandle handle, uint albumId); // LIBMTP_album_t* + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int LIBMTP_Create_New_Album(MtpDeviceHandle handle, ref AlbumStruct album); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + static extern int LIBMTP_Update_Album(MtpDeviceHandle handle, ref AlbumStruct album); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct AlbumStruct + { + public uint album_id; + public uint parent_id; + public uint storage_id; + + [MarshalAs(UnmanagedType.LPStr)] + public string name; + + [MarshalAs(UnmanagedType.LPStr)] + public string artist; + + [MarshalAs(UnmanagedType.LPStr)] + public string composer; + + [MarshalAs(UnmanagedType.LPStr)] + public string genre; + + public IntPtr tracks; + public uint no_tracks; + + public IntPtr next; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Error.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Error.cs new file mode 100644 index 00000000..67820352 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Error.cs @@ -0,0 +1,85 @@ +/*************************************************************************** + * Error.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public class LibMtpException : Exception + { + public LibMtpException(ErrorCode error) : this(error, error.ToString(), null) + { + } + + public LibMtpException(ErrorCode error, string message) : this(error, message, null) + { + + } + + public LibMtpException(ErrorCode error, string message, Exception innerException) + : base(message, innerException) + { + } + + internal static void CheckErrorStack(MtpDeviceHandle handle) + { + IntPtr ptr = MtpDevice.GetErrorStack(handle); + if (ptr == IntPtr.Zero) + return; + + LibMtpException ex = null; + while (ptr != IntPtr.Zero) + { + Error e = (Error)Marshal.PtrToStructure(ptr, typeof(Error)); + ex = new LibMtpException(e.errornumber, e.error_text, ex); + ptr = e.next; + } + + // Once we throw the exception, clear the error stack + MtpDevice.ClearErrorStack(handle); + throw ex; + } + } + + public struct Error + { + public ErrorCode errornumber; + [MarshalAs(UnmanagedType.LPStr)] public string error_text; + public IntPtr next; // LIBMTP_error_t* + + public static void CheckError(ErrorCode errorCode) + { + if (errorCode != ErrorCode.None) + { + throw new LibMtpException(errorCode, errorCode.ToString()); + } + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/ErrorCode.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/ErrorCode.cs new file mode 100644 index 00000000..e8b2f8f8 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/ErrorCode.cs @@ -0,0 +1,57 @@ +/*************************************************************************** + * MtpDevice.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public enum ErrorCode + { + /*LIBMTP_ERROR_NONE, + LIBMTP_ERROR_GENERAL, + LIBMTP_ERROR_PTP_LAYER, + LIBMTP_ERROR_USB_LAYER, + LIBMTP_ERROR_MEMORY_ALLOCATION, + LIBMTP_ERROR_NO_DEVICE_ATTACHED, + LIBMTP_ERROR_STORAGE_FULL, + LIBMTP_ERROR_CONNECTING, + LIBMTP_ERROR_CANCELLED*/ + None, + General, + PtpLayer, + UsbLayer, + MemoryAllocation, + NoDeviceAttached, + StorageFull, + Connecting, + Cancelled + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/File.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/File.cs new file mode 100644 index 00000000..871b04bf --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/File.cs @@ -0,0 +1,90 @@ +/*************************************************************************** + * File.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public class File + { + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_new_file_t(); // LIBMTP_file_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern void LIBMTP_destroy_file_t(ref File file); // LIBMTP_file_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern string LIBMTP_Get_Filetype_Description(FileType type); // char const * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Filelisting(MtpDeviceHandle handle); // LIBMTP_file_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Filelisting_With_Callback(MtpDeviceHandle handle, ProgressFunction function, IntPtr data); // LIBMTP_file_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Filemetadata(MtpDeviceHandle handle, uint fileid); // LIBMTP_file_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Get_File_To_File(MtpDeviceHandle handle, uint fileId, string path, ProgressFunction function, IntPtr data); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern void LIBMTP_destroy_filesampledata_t(ref FileSampleData data); // LIBMTP_filesampledata_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Get_Representative_Sample_Format(MtpDeviceHandle handle, FileType type, IntPtr data_array); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Send_Representative_Sample(MtpDeviceHandle handle, uint id, ref FileSampleData sample); + + } + + [StructLayout(LayoutKind.Sequential)] + internal struct FileStruct + { + public int item_id; + public int parent_id; + public int storage_id; + + [MarshalAs(UnmanagedType.LPStr)] public string filename; + public long filesize; + public FileType filetype; + public IntPtr next; // LIBMTP_file_t* + public File NextFile + { + get + { + if (next == IntPtr.Zero) + return null; + return (File)Marshal.PtrToStructure(next, typeof(File)); + } + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/FileSampleData.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/FileSampleData.cs new file mode 100644 index 00000000..daf71d9b --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/FileSampleData.cs @@ -0,0 +1,57 @@ +/*************************************************************************** + * FileSampleData.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Mtp +{ + internal static class FileSample + { + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + public static extern void LIBMTP_destroy_filesampledata_t(ref FileSampleData data); // LIBMTP_filesampledata_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + public static extern int LIBMTP_Get_Representative_Sample_Format(MtpDeviceHandle handle, FileType type, IntPtr data_array); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + public static extern int LIBMTP_Send_Representative_Sample(MtpDeviceHandle handle, uint id, ref FileSampleData sample); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct FileSampleData + { + public uint width; + public uint height; + public uint duration; + public FileType filetype; + public ulong size; + public IntPtr data; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/FileType.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/FileType.cs new file mode 100644 index 00000000..eaa8d43e --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/FileType.cs @@ -0,0 +1,82 @@ +/*************************************************************************** + * Enums.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; + +namespace Mtp +{ + public enum FileType + { + FOLDER, + WAV, + MP3, + WMA, + OGG, + AUDIBLE, + MP4, + UNDEF_AUDIO, + WMV, + AVI, + MPEG, + ASF, + QT, + UNDEF_VIDEO, + JPEG, + JFIF, + TIFF, + BMP, + GIF, + PICT, + PNG, + VCALENDAR1, + VCALENDAR2, + VCARD2, + VCARD3, + WINDOWSIMAGEFORMAT, + WINEXEC, + TEXT, + HTML, + FIRMWARE, + AAC, + MEDIACARD, + FLAC, + MP2, + M4A, + DOC, + XML, + XLS, + PPT, + MHT, + JP2, + JPX, + ALBUM, + PLAYLIST, + UNKNOWN + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Folder.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Folder.cs new file mode 100644 index 00000000..936a0417 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Folder.cs @@ -0,0 +1,313 @@ +/*************************************************************************** + * Folder.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public class Folder + { + private MtpDevice device; + private uint folderId; + private uint parentId; + private string name; + + internal uint FolderId + { + get { return folderId; } + } + + public string Name + { + get { return name; } + } + + internal uint ParentId + { + get { return parentId; } + } + + internal Folder(uint folderId, uint parentId, string name, MtpDevice device) + { + this.device = device; + this.folderId = folderId; + this.parentId = parentId; + this.name = name; + } + + internal Folder(FolderStruct folder, MtpDevice device) + : this(folder.folder_id, folder.parent_id, folder.name, device) + { + + } + + public Folder AddChild(string name) + { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException("name"); + + // First create the folder on the device and check for error + uint id = CreateFolder(device.Handle, name, FolderId); + + FolderStruct f = new FolderStruct(); + f.folder_id = id; + f.parent_id = FolderId; + f.name = name; + + return new Folder(f, device); + } + + public List GetChildren() + { + IntPtr root = GetFolderList(device.Handle); + IntPtr ptr = Find(root, folderId); + + FolderStruct f = (FolderStruct)Marshal.PtrToStructure(ptr, typeof(FolderStruct)); + + ptr = f.child; + List folders = new List(); + while (ptr != IntPtr.Zero) + { + FolderStruct folder = (FolderStruct)Marshal.PtrToStructure(ptr, typeof(FolderStruct)); + folders.Add(new Folder(folder, device)); + ptr = folder.sibling; + } + + LIBMTP_destroy_folder_t(root); + return folders; + } + + private Folder CreateParentFolder() + { + return Find(device, parentId); + } + + public bool HasAncestor(Folder ancestor) + { + if (ancestor == null) + { + throw new ArgumentNullException("ancestor"); + } + + if (device != ancestor.device) + { + throw new ArgumentException("Folders are on different devices"); + } + + bool hasAncestor = false; + if (parentId != 0) + { + if (parentId == ancestor.FolderId) + { + hasAncestor = true; + } + else + { + Folder parent = CreateParentFolder(); + + hasAncestor = parent.HasAncestor(ancestor); + } + } + return hasAncestor; + } + + public void Remove() + { + MtpDevice.DeleteObject(device.Handle, FolderId); + } + + public override string ToString() + { + return String.Format("{0} (id {1}, parent id {2})", Name, FolderId, ParentId); + } + + internal static List GetRootFolders(MtpDevice device) + { + List folders = new List(); + IntPtr root = GetFolderList(device.Handle); + try + { + for (IntPtr ptr = root; ptr != IntPtr.Zero;) + { + FolderStruct folder = (FolderStruct)Marshal.PtrToStructure(ptr, typeof(FolderStruct)); + folders.Add(new Folder(folder, device)); + ptr = folder.sibling; + } + } + finally + { + // Recursively destroy the folder tree + LIBMTP_destroy_folder_t(root); + } + return folders; + } + + internal static uint CreateFolder(MtpDeviceHandle handle, string name, uint parentId) + { + uint result = LIBMTP_Create_Folder(handle, name, parentId, 0); + if (result == 0) + { + LibMtpException.CheckErrorStack(handle); + throw new LibMtpException(ErrorCode.General, "Could not create folder on the device"); + } + + return result; + } + + internal static void DestroyFolder(IntPtr folder) + { + LIBMTP_destroy_folder_t(folder); + } + + internal static IntPtr Find(IntPtr folderList, uint folderId) + { + return LIBMTP_Find_Folder(folderList, folderId); + } + + internal static Folder Find(MtpDevice device, uint folderId) + { + if (device == null) + { + throw new ArgumentNullException("device"); + } + + Folder folder = null; + IntPtr root = GetFolderList(device.Handle); + try + { + IntPtr ptr = Find(root, folderId); + if (ptr != IntPtr.Zero) + { + FolderStruct folderStruct = (FolderStruct)Marshal.PtrToStructure(ptr, typeof(FolderStruct)); + folder = new Folder(folderStruct, device); + } + } + finally + { + DestroyFolder(root); + } + return folder; + } + + internal static IntPtr GetFolderList(MtpDeviceHandle handle) + { + return LIBMTP_Get_Folder_List(handle); + } + + // Folder Management + //[DllImport (MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern IntPtr LIBMTP_new_folder_t (); // LIBMTP_folder_t* + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern void LIBMTP_destroy_folder_t(IntPtr folder); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Folder_List(MtpDeviceHandle handle); // LIBMTP_folder_t* + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Find_Folder(IntPtr folderList, uint folderId); // LIBMTP_folder_t* + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern uint LIBMTP_Create_Folder(MtpDeviceHandle handle, string name, uint parentId, uint storageId); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct FolderStruct + { + public uint folder_id; + public uint parent_id; + public uint storage_id; + + [MarshalAs(UnmanagedType.LPStr)] public string name; + public IntPtr sibling; // LIBMTP_folder_t* + public IntPtr child; // LIBMTP_folder_t* + /* + public object NextSibling + { + get + { + if(sibling == IntPtr.Zero) + return null; + return (FolderStruct)Marshal.PtrToStructure(sibling, typeof(Folder)); + } + } + + public object NextChild + { + get + { + if(child == IntPtr.Zero) + return null; + return (FolderStruct)Marshal.PtrToStructure(child, typeof(Folder)); + } + } + + public Folder? Sibling + { + get + { + if (sibling == IntPtr.Zero) + return null; + return (Folder)Marshal.PtrToStructure(sibling, typeof(Folder)); + } + } + + public Folder? Child + { + get + { + if (child == IntPtr.Zero) + return null; + return (Folder)Marshal.PtrToStructure(child, typeof(Folder)); + } + }*/ + + /*public IEnumerable Children() + { + Folder? current = Child; + while(current.HasValue) + { + yield return current.Value; + current = current.Value.Child; + } + }*/ + + /*public IEnumerable Siblings() + { + Folder? current = Sibling; + while(current.HasValue) + { + yield return current.Value; + current = current.Value.Sibling; + } + }*/ + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/MtpDevice.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/MtpDevice.cs new file mode 100644 index 00000000..7435c7b6 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/MtpDevice.cs @@ -0,0 +1,642 @@ +/*************************************************************************** + * MtpDevice.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public delegate int ProgressFunction(ulong sent, ulong total, IntPtr data); + + public class MtpDevice : IDisposable + { + internal static IntPtr Offset(IntPtr ptr, int offset) + { + if (IntPtr.Size == 8) + { + return (IntPtr)(ptr.ToInt64() + offset); + } + else + { + return (IntPtr)(ptr.ToInt32() + offset); + } + } + + internal MtpDeviceHandle Handle; + private MtpDeviceStruct device; + private string name; + private Folder albumFolder; + private Folder musicFolder; + private Folder organizerFolder; + private Folder pictureFolder; + private Folder playlistFolder; + private Folder podcastFolder; + private Folder textFolder; + private Folder videoFolder; + + static MtpDevice() + { + LIBMTP_Init(); + } + + public int BatteryLevel + { + get + { + ushort level, maxLevel; + GetBatteryLevel(Handle, out maxLevel, out level); + return (int)((level * 100.0) / maxLevel); + } + } + + public string SerialNumber + { + get { return GetSerialnumber(Handle); } + } + + public string Version + { + get { return GetDeviceversion(Handle); } + } + + public string ModelName + { + get; private set; + } + + public string Name + { + get { return name; } + set + { + if (SetFriendlyName(Handle, value)) + { + name = value; + } + } + } + + public Folder AlbumFolder + { + get { return albumFolder; } + } + + public Folder MusicFolder + { + get { return musicFolder; } + } + + public Folder OrganizerFolder + { + get { return organizerFolder; } + } + + public Folder PictureFolder + { + get { return pictureFolder; } + } + + public Folder PlaylistFolder + { + get { return playlistFolder; } + } + + public Folder PodcastFolder + { + get { return podcastFolder; } + } + + public Folder TextFolder + { + get { return textFolder; } + } + + public Folder VideoFolder + { + get { return videoFolder; } + } + + internal MtpDevice(MtpDeviceHandle handle, MtpDeviceStruct device) + { + this.device = device; + this.Handle = handle; + this.name = GetFriendlyName(Handle); + this.ModelName = GetModelName(Handle); + SetDefaultFolders(); + } + + internal MtpDevice(IntPtr handle, bool ownsHandle, MtpDeviceStruct device) + : this(new MtpDeviceHandle(handle, ownsHandle), device) + { + + } + + /// + /// This function scans the top level directories and stores the relevant ones so they are readily + /// accessible + /// + private void SetDefaultFolders() + { + List folders = GetRootFolders(); + + foreach (Folder f in folders) + { + if (f.FolderId == this.device.default_album_folder) + albumFolder = f; + else if (f.FolderId == device.default_music_folder) + { + musicFolder = f; + } + else if (f.FolderId == device.default_organizer_folder) + organizerFolder = f; + else if (f.FolderId == device.default_picture_folder) + pictureFolder = f; + else if (f.FolderId == device.default_playlist_folder) + playlistFolder = f; + else if (f.FolderId == device.default_text_folder) + textFolder = f; + else if (f.FolderId == device.default_video_folder) + videoFolder = f; + else if (f.FolderId == device.default_zencast_folder) + podcastFolder = f; + } + + // Fix for devices that don't have an explicit playlist folder (see BGO #590342 and #733883) + if (playlistFolder == null && musicFolder != null) + { + playlistFolder = musicFolder; + } + } + + public void Dispose() + { + if (!Handle.IsClosed) + Handle.Close(); + } + + public List GetRootFolders() + { + return Folder.GetRootFolders(this); + } + + public List GetAllTracks() + { + return GetAllTracks(null); + } + + public List GetAllTracks(ProgressFunction callback) + { + IntPtr ptr = Track.GetTrackListing(Handle, callback, IntPtr.Zero); + + List tracks = new List(); + + while (ptr != IntPtr.Zero) + { + // Destroy the struct after we use it to avoid potential referencing of freed memory. + TrackStruct track = (TrackStruct)Marshal.PtrToStructure(ptr, typeof(TrackStruct)); + tracks.Add(new Track(track, this)); + Track.DestroyTrack(ptr); + ptr = track.next; + } + + return tracks; + } + + public List GetPlaylists() + { + return Playlist.GetPlaylists(this); + } + + public List GetAlbums() + { + return Album.GetAlbums(this); + } + + public List GetStorage() + { + List storages = new List(); + IntPtr ptr = device.storage; + while (ptr != IntPtr.Zero) + { + DeviceStorage storage = (DeviceStorage)Marshal.PtrToStructure(ptr, typeof(DeviceStorage)); + storages.Add(storage); + ptr = storage.Next; + } + return storages; + } + + public void Remove(Track track) + { + DeleteObject(Handle, track.FileId); + } + + public void UploadTrack(string path, Track track, Folder folder) + { + UploadTrack(path, track, folder, null); + } + + public void UploadTrack(string path, Track track, Folder folder, ProgressFunction callback) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentNullException("path"); + if (track == null) + throw new ArgumentNullException("track"); + + folder = folder ?? MusicFolder; + if (folder != null) + { + track.trackStruct.parent_id = folder.FolderId; + } + + // We send the trackstruct by ref so that when the file_id gets filled in, our copy is updated + Track.SendTrack(Handle, path, ref track.trackStruct, callback, IntPtr.Zero); + // LibMtp.GetStorage (Handle, 0); + } + + public FileType[] GetFileTypes() + { + Int16[] ints = GetFileTypes(Handle); + FileType[] file_types = new FileType[ints.Length]; + for (int i = 0; i < ints.Length; i++) + { + file_types[i] = (FileType)ints[i]; + } + + return file_types; + } + + public static MtpDevice Connect(RawMtpDevice rawDevice) + { + var raw = rawDevice.RawDevice; + IntPtr device = LIBMTP_Open_Raw_Device(raw); + if (device == IntPtr.Zero) + return null; + return new MtpDevice(new MtpDeviceHandle(device, true), (MtpDeviceStruct)Marshal.PtrToStructure(device, typeof(MtpDeviceStruct))); + } + + public static List Detect() + { + int count = 0; + IntPtr ptr = IntPtr.Zero; + LIBMTP_Detect_Raw_Devices(ref ptr, ref count); + + List devices = new List(); + for (int i = 0; i < count; i++) + { + IntPtr offset = Offset(ptr, i * Marshal.SizeOf(typeof(RawDeviceStruct))); + RawDeviceStruct d = (RawDeviceStruct)Marshal.PtrToStructure(offset, typeof(RawDeviceStruct)); + devices.Add(new RawMtpDevice(d)); + } + + return devices; + } + + internal static void ClearErrorStack(MtpDeviceHandle handle) + { + LIBMTP_Clear_Errorstack(handle); + } + + internal static void DeleteObject(MtpDeviceHandle handle, uint object_id) + { + if (LIBMTP_Delete_Object(handle, object_id) != 0) + { + LibMtpException.CheckErrorStack(handle); + throw new LibMtpException(ErrorCode.General, "Could not delete the track"); + } + } + + internal static void GetBatteryLevel(MtpDeviceHandle handle, out ushort maxLevel, out ushort currentLevel) + { + int result = LIBMTP_Get_Batterylevel(handle, out maxLevel, out currentLevel); + if (result != 0) + { + LibMtpException.CheckErrorStack(handle); + throw new LibMtpException(ErrorCode.General, "Could not retrieve battery stats"); + } + } + + internal static void GetConnectedDevices(out IntPtr list) + { + Error.CheckError(LIBMTP_Get_Connected_Devices(out list)); + } + + internal static IntPtr GetErrorStack(MtpDeviceHandle handle) + { + return LIBMTP_Get_Errorstack(handle); + } + + internal static string GetDeviceversion(MtpDeviceHandle handle) + { + IntPtr ptr = LIBMTP_Get_Deviceversion(handle); + if (ptr == IntPtr.Zero) + return null; + + return StringFromIntPtr(ptr); + } + + + internal static string GetFriendlyName(MtpDeviceHandle handle) + { + IntPtr ptr = LIBMTP_Get_Friendlyname(handle); + if (ptr == IntPtr.Zero) + return null; + + return StringFromIntPtr(ptr); + } + + internal static bool SetFriendlyName(MtpDeviceHandle handle, string name) + { + bool success = LIBMTP_Set_Friendlyname(handle, name) == 0; + return success; + } + + internal static string GetModelName(MtpDeviceHandle handle) + { + IntPtr ptr = LIBMTP_Get_Modelname(handle); + if (ptr == IntPtr.Zero) + return null; + + return StringFromIntPtr(ptr); + } + + internal static string GetSerialnumber(MtpDeviceHandle handle) + { + IntPtr ptr = LIBMTP_Get_Serialnumber(handle); + if (ptr == IntPtr.Zero) + return null; + + return StringFromIntPtr(ptr); + } + + internal static void GetStorage(MtpDeviceHandle handle, int sortMode) + { + LIBMTP_Get_Storage(handle, sortMode); + } + + internal static Int16[] GetFileTypes(MtpDeviceHandle handle) + { + IntPtr types = IntPtr.Zero; + ushort count = 0; + if (LIBMTP_Get_Supported_Filetypes(handle, ref types, ref count) == 0) + { + Int16[] type_ary = new Int16[count]; + Marshal.Copy(types, type_ary, 0, (int)count); + Marshal.FreeHGlobal(types); + return type_ary; + } + + return new Int16[0]; + } + + internal static void ReleaseDevice(IntPtr handle) + { + LIBMTP_Release_Device(handle); + } + + private static string StringFromIntPtr(IntPtr ptr) + { + int i = 0; + while (Marshal.ReadByte(ptr, i) != (byte)0) ++i; + byte[] s_buf = new byte[i]; + Marshal.Copy(ptr, s_buf, 0, s_buf.Length); + string s = System.Text.Encoding.UTF8.GetString(s_buf); + Marshal.FreeCoTaskMem(ptr); + return s; + } + + internal const string LibMtpLibrary = "libmtp.dll"; + + // Device Management + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern void LIBMTP_Init(); + + // Clears out the error stack and frees any allocated memory. + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern void LIBMTP_Clear_Errorstack(MtpDeviceHandle handle); + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int LIBMTP_Delete_Object(MtpDeviceHandle handle, uint object_id); + + // Gets the first connected device: + //[DllImport (LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern IntPtr LIBMTP_Get_First_Device (); // LIBMTP_mtpdevice_t * + + // Gets the storage information + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Get_Storage(MtpDeviceHandle handle, int sortMode); + + // Formats the supplied storage device attached to the device + //[DllImport (LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern int LIBMTP_Format_Storage (MtpDeviceHandle handle, ref DeviceStorage storage); + + // Counts the devices in the list + //[DllImport (LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern uint LIBMTP_Number_Devices_In_List (MtpDeviceHandle handle); + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern ErrorCode LIBMTP_Get_Connected_Devices(out IntPtr list); //LIBMTP_mtpdevice_t ** + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern ErrorCode LIBMTP_Detect_Raw_Devices(ref IntPtr list, ref int count); //LIBMTP_raw_device_t + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Open_Raw_Device(RawDeviceStruct rawdevice); + + // Deallocates the memory for the device + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern void LIBMTP_Release_Device(IntPtr device); + + //[DllImport (LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern int LIBMTP_Reset_Device (MtpDeviceHandle handle); + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Get_Batterylevel(MtpDeviceHandle handle, out ushort maxLevel, out ushort currentLevel); + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Modelname(MtpDeviceHandle handle); // char * + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Serialnumber(MtpDeviceHandle handle); // char * + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Deviceversion(MtpDeviceHandle handle); // char * + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Friendlyname(MtpDeviceHandle handle); // char * + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Set_Friendlyname(MtpDeviceHandle handle, string name); + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Errorstack(MtpDeviceHandle handle); // LIBMTP_error_t * + + [DllImport(LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Get_Supported_Filetypes(MtpDeviceHandle handle, ref IntPtr types, ref ushort count); // uint16_t **const + + + // void LIBMTP_Release_Device_List (LIBMTP_mtpdevice_t *) + + + // int LIBMTP_Detect_Descriptor (uint16_t *, uint16_t *); + /* + void LIBMTP_Dump_Device_Info (LIBMTP_mtpdevice_t *) + + char * LIBMTP_Get_Syncpartner (LIBMTP_mtpdevice_t *) + int LIBMTP_Set_Syncpartner (LIBMTP_mtpdevice_t *, char const *const) + int LIBMTP_Get_Secure_Time (LIBMTP_mtpdevice_t *, char **const) + int LIBMTP_Get_Device_Certificate (LIBMTP_mtpdevice_t *, char **const) + */ + + public static string GetMimeTypeFor(FileType type) + { + switch (type) + { + case FileType.MP3: return "audio/mpeg"; + case FileType.OGG: return "audio/ogg"; + case FileType.WMA: return "audio/x-ms-wma"; + case FileType.WMV: return "video/x-ms-wmv"; + case FileType.ASF: return "video/x-ms-asf"; + case FileType.AAC: return "audio/x-aac"; + case FileType.MP4: return "video/mp4"; + case FileType.AVI: return "video/avi"; + case FileType.WAV: return "audio/x-wav"; + case FileType.MPEG: return "video/mpeg"; + case FileType.FLAC: return "audio/flac"; + case FileType.QT: return "video/quicktime"; + case FileType.M4A: return "audio/mp4"; + } + return null; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct DeviceEntry + { + [MarshalAs(UnmanagedType.LPStr)] public string vendor; + public ushort vendor_id; + [MarshalAs(UnmanagedType.LPStr)] public string product; + public ushort product_id; + public uint device_flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DeviceStorage + { + public uint Id; + public ushort StorageType; + public ushort FileSystemType; + public ushort AccessCapability; + public ulong MaxCapacity; + public ulong FreeSpaceInBytes; + public ulong FreeSpaceInObjects; + [MarshalAs(UnmanagedType.LPStr)] public string StorageDescription; + [MarshalAs(UnmanagedType.LPStr)] public string VolumeIdentifier; + public IntPtr Next; // LIBMTP_devicestorage_t* + public IntPtr Prev; // LIBMTP_devicestorage_t* + } + + internal class MtpDeviceHandle : SafeHandle + { + private MtpDeviceHandle() + : base(IntPtr.Zero, true) + { + + } + + internal MtpDeviceHandle(IntPtr ptr, bool ownsHandle) + : base(IntPtr.Zero, ownsHandle) + { + SetHandle(ptr); + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override bool ReleaseHandle() + { + MtpDevice.ReleaseDevice(handle); + return true; + } + } + + internal struct MtpDeviceStruct + { + public byte object_bitsize; + public IntPtr parameters; // void* + public IntPtr usbinfo; // void* + public IntPtr storage; // LIBMTP_devicestorage_t* + public IntPtr errorstack; // LIBMTP_error_t* + public byte maximum_battery_level; + public uint default_music_folder; + public uint default_playlist_folder; + public uint default_picture_folder; + public uint default_video_folder; + public uint default_organizer_folder; + public uint default_zencast_folder; + public uint default_album_folder; + public uint default_text_folder; + public IntPtr cd; // void* + public IntPtr next; // LIBMTP_mtpdevice_t* + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RawDeviceStruct + { + public DeviceEntry device_entry; /**< The device entry for this raw device */ + public uint bus_location; /**< Location of the bus, if device available */ + public byte devnum; /**< Device number on the bus, if device available */ + } + + public class RawMtpDevice + { + + public uint BusNumber + { + get { return RawDevice.bus_location; } + } + + public int DeviceNumber + { + get { return RawDevice.devnum; } + } + + internal RawDeviceStruct RawDevice + { + get; private set; + } + + internal RawMtpDevice(RawDeviceStruct device) + { + RawDevice = device; + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Playlist.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Playlist.cs new file mode 100644 index 00000000..0d3c8d0a --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Playlist.cs @@ -0,0 +1,145 @@ +/*************************************************************************** + * Playlist.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public sealed class Playlist : AbstractTrackList + { + internal static List GetPlaylists(MtpDevice device) + { + List playlists = new List(); + IntPtr ptr = Playlist.LIBMTP_Get_Playlist_List(device.Handle); + while (ptr != IntPtr.Zero) + { + // Destroy the struct *after* we use it to ensure we don't access freed memory + // for the 'tracks' variable + PlaylistStruct d = (PlaylistStruct)Marshal.PtrToStructure(ptr, typeof(PlaylistStruct)); + playlists.Add(new Playlist(device, d)); + LIBMTP_destroy_playlist_t(ptr); + ptr = d.next; + } + return playlists; + } + + private PlaylistStruct playlist; + + public override uint Count + { + get { return playlist.no_tracks; } + protected set { playlist.no_tracks = value; } + } + + public override string Name + { + get { return playlist.Name; } + set { playlist.Name = value; } + } + + protected override IntPtr TracksPtr + { + get { return playlist.tracks; } + set { playlist.tracks = value; } + } + + public Playlist(MtpDevice device, string name) : base(device) + { + this.playlist = new PlaylistStruct(); + Name = name; + Count = 0; + } + + internal Playlist(MtpDevice device, PlaylistStruct playlist) : base(device, playlist.tracks, playlist.no_tracks) + { + // Once we've loaded the tracks, set the TracksPtr to NULL as it + // will be freed when the Playlist constructor is finished. + this.playlist = playlist; + TracksPtr = IntPtr.Zero; + } + + protected override int Create() + { + playlist.parent_id = Device.PlaylistFolder.FolderId; + return LIBMTP_Create_New_Playlist(Device.Handle, ref playlist); + } + + protected override int Update() + { + return LIBMTP_Update_Playlist(Device.Handle, ref playlist); + } + + public void Remove() + { + MtpDevice.LIBMTP_Delete_Object(Device.Handle, playlist.playlist_id); + } + + // Playlist Management + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern void LIBMTP_destroy_playlist_t(IntPtr playlist); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Playlist_List(MtpDeviceHandle handle); // LIBMTP_playlist_t* + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Create_New_Playlist(MtpDeviceHandle handle, ref PlaylistStruct metadata); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Update_Playlist(MtpDeviceHandle handle, ref PlaylistStruct playlist); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PlaylistStruct + { + public uint playlist_id; + public uint parent_id; + public uint storage_id; + + [MarshalAs(UnmanagedType.LPStr)] + public string Name; + + public IntPtr tracks; // int* + public uint no_tracks; + public IntPtr next; // LIBMTP_playlist_t* + + + /*public Playlist? Next + { + get + { + if (next == IntPtr.Zero) + return null; + return (Playlist)Marshal.PtrToStructure(next, typeof(Playlist)); + } + }*/ + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Track.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Track.cs new file mode 100644 index 00000000..bfeaa97b --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/LibMTP/Track.cs @@ -0,0 +1,344 @@ +/*************************************************************************** + * Track.cs + * + * Copyright (C) 2006-2007 Alan McGovern + * Authors: + * Alan McGovern (alan.mcgovern@gmail.com) + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Mtp +{ + public class Track + { + internal TrackStruct trackStruct; + private MtpDevice device; + + public uint FileId + { + get { return trackStruct.item_id; } + } + + public string Album + { + get { return trackStruct.album; } + set { trackStruct.album = value; } + } + + public string Artist + { + get { return trackStruct.artist; } + set { trackStruct.artist = value; } + } + + public uint Bitrate + { + get { return trackStruct.bitrate; } + } + + public ushort BitrateType + { + get { return trackStruct.bitratetype; } + } + + public string ReleaseDate + { + get { return trackStruct.date; } + set { trackStruct.date = value; } + } + + public int Year + { + get { return ReleaseDate == null || ReleaseDate.Length < 4 ? 0 : Int32.Parse(ReleaseDate.Substring(0, 4)); } + set { ReleaseDate = String.Format("{0:0000}0101T0000.00", value); } + } + + public uint Duration + { + get { return trackStruct.duration; } + set { trackStruct.duration = value; } + } + + public string FileName + { + get { return trackStruct.filename; } + set { trackStruct.filename = value; } + } + + public ulong FileSize + { + get { return trackStruct.filesize; } + set { trackStruct.filesize = value; } + } + + public FileType FileType + { + get { return trackStruct.filetype; } + set { trackStruct.filetype = value; } + } + + public string Genre + { + get { return trackStruct.genre; } + set { trackStruct.genre = value; } + } + + public ushort NoChannels + { + get { return trackStruct.nochannels; } + set { trackStruct.nochannels = value; } + } + + // 0 to 100 + public ushort Rating + { + get { return trackStruct.rating; } + set + { + if (value < 0 || value > 100) + throw new ArgumentOutOfRangeException("Rating", "Rating must be between zero and 100"); + trackStruct.rating = value; + } + } + + public uint SampleRate + { + get { return trackStruct.samplerate; } + set { trackStruct.samplerate = value; } + } + + public string Title + { + get { return trackStruct.title; } + set { trackStruct.title = value; } + } + + public ushort TrackNumber + { + get { return trackStruct.tracknumber; } + set { trackStruct.tracknumber = value; } + } + + public uint WaveCodec + { + get { return trackStruct.wavecodec; } + } + + public uint UseCount + { + get { return trackStruct.usecount; } + set { trackStruct.usecount = value; } + } + + public string Composer + { + get { return trackStruct.composer; } + set { trackStruct.composer = value; } + } + + public Track(string filename, ulong filesize) : this(filename, filesize, null) + { + } + + public Track(string filename, ulong filesize, MtpDevice device) : this(new TrackStruct(), device) + { + this.trackStruct.filename = filename; + this.trackStruct.filesize = filesize; + this.trackStruct.filetype = DetectFileType(this); + } + + internal Track(TrackStruct track, MtpDevice device) + { + this.device = device; + this.trackStruct = track; + } + + public bool InFolder(Folder folder) + { + return InFolder(folder, false); + } + + public bool InFolder(Folder folder, bool recursive) + { + if (folder == null) + { + return false; + } + + bool is_parent = trackStruct.parent_id == folder.FolderId; + if (is_parent || !recursive) + { + return is_parent; + } + + return Folder.Find(device, trackStruct.parent_id).HasAncestor(folder); + } + + public void Download(string path) + { + Download(path, null); + } + + public void Download(string path, ProgressFunction callback) + { + if (String.IsNullOrEmpty(path)) + throw new ArgumentException("Cannot be null or empty", "path"); + + GetTrack(device.Handle, trackStruct.item_id, path, callback, IntPtr.Zero); + } + + public void UpdateMetadata() + { + UpdateTrackMetadata(device.Handle, ref trackStruct); + } + + private static FileType DetectFileType(Track track) + { + string ext = System.IO.Path.GetExtension(track.FileName); + + // Strip leading . + if (ext.Length > 0) + ext = ext.Substring(1, ext.Length - 1); + + // this is a hack; catch all m4(a|b|v|p) + if (ext != null && ext.ToLower().StartsWith("m4")) + ext = "mp4"; + + FileType type = (FileType)Enum.Parse(typeof(FileType), ext, true); + //if (type == null) + // return FileType.UNKNOWN; + return type; + } + + internal static void DestroyTrack(IntPtr track) + { + LIBMTP_destroy_track_t(track); + } + + internal static void GetTrack(MtpDeviceHandle handle, uint trackId, string destPath, ProgressFunction callback, IntPtr data) + { + if (LIBMTP_Get_Track_To_File(handle, trackId, destPath, callback, data) != 0) + { + LibMtpException.CheckErrorStack(handle); + throw new LibMtpException(ErrorCode.General, "Could not download track from the device"); + } + } + + internal static IntPtr GetTrackListing(MtpDeviceHandle handle, ProgressFunction function, IntPtr data) + { + return LIBMTP_Get_Tracklisting_With_Callback(handle, function, data); + } + + internal static void SendTrack(MtpDeviceHandle handle, string path, ref TrackStruct metadata, ProgressFunction callback, IntPtr data) + { + if (LIBMTP_Send_Track_From_File(handle, path, ref metadata, callback, data) != 0) + { + LibMtpException.CheckErrorStack(handle); + throw new LibMtpException(ErrorCode.General, "Could not upload the track"); + } + } + + internal static void UpdateTrackMetadata(MtpDeviceHandle handle, ref TrackStruct metadata) + { + if (LIBMTP_Update_Track_Metadata(handle, ref metadata) != 0) + throw new LibMtpException(ErrorCode.General); + } + + //[DllImport (MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern IntPtr LIBMTP_new_track_t (); // LIBMTP_track_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern void LIBMTP_destroy_track_t(IntPtr track); // LIBMTP_track_t * + + //[DllImport (MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern IntPtr LIBMTP_Get_Tracklisting (MtpDeviceHandle handle); //LIBMTP_track_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LIBMTP_Get_Tracklisting_With_Callback(MtpDeviceHandle handle, ProgressFunction callback, IntPtr data); // LIBMTP_track_t * + + //[DllImport (MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern IntPtr LIBMTP_Get_Trackmetadata (MtpDeviceHandle handle, uint trackId); // LIBMTP_track_t * + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Get_Track_To_File(MtpDeviceHandle handle, uint trackId, string path, ProgressFunction callback, IntPtr data); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Send_Track_From_File(MtpDeviceHandle handle, string path, ref TrackStruct track, ProgressFunction callback, IntPtr data); + + [DllImport(MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + private static extern int LIBMTP_Update_Track_Metadata(MtpDeviceHandle handle, ref TrackStruct metadata); + + //[DllImport (MtpDevice.LibMtpLibrary, CallingConvention = CallingConvention.Cdecl)] + //private static extern int LIBMTP_Track_Exists (MtpDeviceHandle handle, uint trackId); + + //int LIBMTP_Get_Track_To_File_Descriptor (MtpDeviceHandle handle, uint trackId, int const, LIBMTP_progressfunc_t const, void const *const) + //int LIBMTP_Send_Track_From_File_Descriptor (MtpDeviceHandle handle, int const, LIBMTP_track_t *const, LIBMTP_progressfunc_t const, void const *const, uint32_t const) + } + + [StructLayout(LayoutKind.Sequential)] + internal struct TrackStruct + { + public uint item_id; + public uint parent_id; + public uint storage_id; + + [MarshalAs(UnmanagedType.LPStr)] public string title; + [MarshalAs(UnmanagedType.LPStr)] public string artist; + [MarshalAs(UnmanagedType.LPStr)] public string composer; + [MarshalAs(UnmanagedType.LPStr)] public string genre; + [MarshalAs(UnmanagedType.LPStr)] public string album; + [MarshalAs(UnmanagedType.LPStr)] public string date; + [MarshalAs(UnmanagedType.LPStr)] public string filename; + + public ushort tracknumber; + public uint duration; + public uint samplerate; + public ushort nochannels; + public uint wavecodec; + public uint bitrate; + public ushort bitratetype; + public ushort rating; // 0 -> 100 + public uint usecount; + public ulong filesize; +#if LIBMTP_SIZEOF_TIME_T_64 + public ulong modificationdate; +#else + public uint modificationdate; +#endif + public FileType filetype; + public IntPtr next; // Track Null if last + /* + public Track? Next + { + get + { + if (next == IntPtr.Zero) + return null; + return (Track)Marshal.PtrToStructure(next, typeof(Track)); + } + }*/ + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/MTPAccessor.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/MTPAccessor.cs new file mode 100644 index 00000000..6fad8e98 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Accessors/MTP/MTPAccessor.cs @@ -0,0 +1,102 @@ +// +// MyClass.cs +// +// Author: +// Mike Becker +// +// Copyright (c) 2019 Mike Becker +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UniversalEditor.IO; + +namespace UniversalEditor.Accessors.MTP +{ + public class MTPAccessor : Accessor + { + private static AccessorReference _ar = null; + + protected override void InitializeInternal() + { + base.InitializeInternal(); + } + + public override long Length { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public override void Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + protected override void CloseInternal() + { + throw new NotImplementedException(); + } + + protected override long GetPosition() + { + return 0; + } + + protected override AccessorReference MakeReferenceInternal() + { + if (_ar == null) + { + _ar = base.MakeReferenceInternal(); + _ar.Title = "Media Transfer Protocol (MTP)"; + _ar.Schemas.Add("mtp-disabled"); + _ar.ImportOptions.Add(new CustomOptionText("FileName", "File _name")); + } + return _ar; + } + + private static IntPtr Offset(IntPtr ptr, int offset) + { + if (IntPtr.Size == 8) + { + return (IntPtr)(ptr.ToInt64() + offset); + } + else + { + return (IntPtr)(ptr.ToInt32() + offset); + } + } + + protected override void OpenInternal() + { + /* + List list = Mtp.MtpDevice.Detect(); + Mtp.RawMtpDevice rawdev = list[0]; + Mtp.MtpDevice dev = Mtp.MtpDevice.Connect(rawdev); + + List listFolders = dev.GetRootFolders(); + */ + // int w = Internal.Methods.Get_File_To_File(hRawDevice, 0, OriginalUri.LocalPath, null, IntPtr.Zero); + + // IntPtr hFile = Internal.Linux.Methods.g_file_new_for_uri(OriginalUri.OriginalString); + } + + protected override int ReadInternal(byte[] buffer, int start, int count) + { + throw new NotImplementedException(); + } + + protected override int WriteInternal(byte[] buffer, int start, int count) + { + throw new NotImplementedException(); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/Properties/AssemblyInfo.cs b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..da734ffd --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// +// AssemblyInfo.cs +// +// Author: +// Mike Becker +// +// Copyright (c) 2019 Mike Becker +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("UniversalEditor.Plugins.MTP")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Mike Becker")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/CSharp/Plugins/UniversalEditor.Plugins.MTP/UniversalEditor.Plugins.MTP.csproj b/CSharp/Plugins/UniversalEditor.Plugins.MTP/UniversalEditor.Plugins.MTP.csproj new file mode 100644 index 00000000..74c260be --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.MTP/UniversalEditor.Plugins.MTP.csproj @@ -0,0 +1,62 @@ + + + + Debug + AnyCPU + {EA724755-2670-4520-86AA-657C8A124DB7} + Library + UniversalEditor + UniversalEditor.Plugins.MTP + v4.7 + + + true + full + false + ..\..\Output\Debug\Plugins + DEBUG; + prompt + 4 + false + + + true + ..\..\Output\Release\Plugins + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + {2D4737E6-6D95-408A-90DB-8DFF38147E85} + UniversalEditor.Core + + + + + + + + + + + \ No newline at end of file diff --git a/CSharp/UniversalEditor.sln b/CSharp/UniversalEditor.sln index f0ba2a28..824c8218 100644 --- a/CSharp/UniversalEditor.sln +++ b/CSharp/UniversalEditor.sln @@ -141,6 +141,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins.Printing", "Plugins EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalEditor.Plugins.Generic.Printing", "Plugins.Printing\UniversalEditor.Plugins.Generic.Printing\UniversalEditor.Plugins.Generic.Printing.csproj", "{A3231B32-A0E4-4B67-9A09-310B25BBB9B6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalEditor.Plugins.MTP", "Plugins\UniversalEditor.Plugins.MTP\UniversalEditor.Plugins.MTP.csproj", "{EA724755-2670-4520-86AA-657C8A124DB7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -405,6 +407,10 @@ Global {A3231B32-A0E4-4B67-9A09-310B25BBB9B6}.Debug|Any CPU.Build.0 = Debug|Any CPU {A3231B32-A0E4-4B67-9A09-310B25BBB9B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {A3231B32-A0E4-4B67-9A09-310B25BBB9B6}.Release|Any CPU.Build.0 = Release|Any CPU + {EA724755-2670-4520-86AA-657C8A124DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA724755-2670-4520-86AA-657C8A124DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA724755-2670-4520-86AA-657C8A124DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA724755-2670-4520-86AA-657C8A124DB7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {6F0AB1AF-E1A1-4D19-B19C-05BBB15C94B2} = {05D15661-E684-4EC9-8FBD-C014BA433CC5} @@ -470,6 +476,7 @@ Global {242A32D8-9A3A-4FE3-902B-AB9C3154723A} = {05D15661-E684-4EC9-8FBD-C014BA433CC5} {868A6888-EDB9-407E-A710-40F297D4EAB8} = {0399182F-AF56-4E86-B229-EAB38C2EE6AF} {A3231B32-A0E4-4B67-9A09-310B25BBB9B6} = {FB678C6D-29A0-405B-BA86-E1EC12A96AA9} + {EA724755-2670-4520-86AA-657C8A124DB7} = {2ED32D16-6C06-4450-909A-40D32DA67FB4} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0