1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8 #include "Win10StorageProvider.h"
9
10 #include "ServiceBroker.h"
11 #include "filesystem/SpecialProtocol.h"
12 #include "guilib/LocalizeStrings.h"
13 #include "storage/MediaManager.h"
14 #include "utils/JobManager.h"
15 #include "utils/StringUtils.h"
16 #include "utils/log.h"
17
18 #include "platform/win10/AsyncHelpers.h"
19 #include "platform/win10/filesystem/WinLibraryDirectory.h"
20 #include "platform/win32/CharsetConverter.h"
21
22 #include <winrt/Windows.Devices.Enumeration.h>
23 #include <winrt/Windows.Foundation.Collections.h>
24 #include <winrt/Windows.Storage.h>
25
26 namespace winrt
27 {
28 using namespace Windows::Foundation;
29 }
30 using namespace winrt::Windows::Devices::Enumeration;
31 using namespace winrt::Windows::Foundation::Collections;
32 using namespace winrt::Windows::Storage;
33
CreateInstance()34 ::IStorageProvider* ::IStorageProvider::CreateInstance()
35 {
36 return new CStorageProvider();
37 }
38
~CStorageProvider()39 CStorageProvider::~CStorageProvider()
40 {
41 if (m_watcher && m_watcher.Status() == DeviceWatcherStatus::Started)
42 m_watcher.Stop();
43 }
44
Initialize()45 void CStorageProvider::Initialize()
46 {
47 m_changed = false;
48 VECSOURCES vShare;
49 GetDrivesByType(vShare, DVD_DRIVES);
50 if (!vShare.empty())
51 CServiceBroker::GetMediaManager().SetHasOpticalDrive(true);
52 else
53 CLog::Log(LOGDEBUG, "%s: No optical drive found.", __FUNCTION__);
54
55 m_watcher = DeviceInformation::CreateWatcher(DeviceClass::PortableStorageDevice);
56 m_watcher.Added([this](auto&&, auto&&)
57 {
58 m_changed = true;
59 });
60 m_watcher.Removed([this](auto&&, auto&&)
61 {
62 m_changed = true;
63 });
64 m_watcher.Updated([this](auto&&, auto&&)
65 {
66 m_changed = true;
67 });
68 m_watcher.Start();
69 }
70
GetLocalDrives(VECSOURCES & localDrives)71 void CStorageProvider::GetLocalDrives(VECSOURCES &localDrives)
72 {
73 CMediaSource share;
74 share.strPath = CSpecialProtocol::TranslatePath("special://home");
75 share.strName = g_localizeStrings.Get(21440);
76 share.m_ignore = true;
77 share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
78 localDrives.push_back(share);
79
80 GetDrivesByType(localDrives, LOCAL_DRIVES, true);
81 }
82
GetRemovableDrives(VECSOURCES & removableDrives)83 void CStorageProvider::GetRemovableDrives(VECSOURCES &removableDrives)
84 {
85 using KODI::PLATFORM::WINDOWS::FromW;
86
87 // get drives which we have direct access for (in case of broad file system access)
88 GetDrivesByType(removableDrives, REMOVABLE_DRIVES, true);
89
90 try
91 {
92 auto devicesView = Wait(winrt::Windows::Storage::KnownFolders::RemovableDevices().GetFoldersAsync());
93 for (unsigned i = 0; i < devicesView.Size(); i++)
94 {
95 auto device = devicesView.GetAt(i);
96 if (device.Path().empty())
97 continue;
98 CMediaSource source;
99 source.strName = FromW(device.DisplayName().c_str());
100 std::string driveLetter = FromW(device.Path().c_str());
101
102 // skip exiting in case if we have direct access
103 auto exiting = std::find_if(removableDrives.begin(), removableDrives.end(), [&driveLetter](CMediaSource& m) {
104 return m.strPath == driveLetter;
105 });
106 if (exiting != removableDrives.end())
107 continue;
108
109 UINT uDriveType = GetDriveTypeA(driveLetter.c_str());
110 source.strPath = "win-lib://removable/" + driveLetter + "/";
111 source.m_iDriveType = (
112 (uDriveType == DRIVE_FIXED) ? CMediaSource::SOURCE_TYPE_LOCAL :
113 (uDriveType == DRIVE_REMOTE) ? CMediaSource::SOURCE_TYPE_REMOTE :
114 (uDriveType == DRIVE_CDROM) ? CMediaSource::SOURCE_TYPE_DVD :
115 (uDriveType == DRIVE_REMOVABLE) ? CMediaSource::SOURCE_TYPE_REMOVABLE :
116 CMediaSource::SOURCE_TYPE_UNKNOWN);
117
118 removableDrives.push_back(source);
119 }
120 }
121 catch (const winrt::hresult_error&)
122 {
123 }
124 }
125
GetFirstOpticalDeviceFileName()126 std::string CStorageProvider::GetFirstOpticalDeviceFileName()
127 {
128 VECSOURCES vShare;
129 std::string strdevice = "\\\\.\\";
130 GetDrivesByType(vShare, DVD_DRIVES);
131
132 if (!vShare.empty())
133 return strdevice.append(vShare.front().strPath);
134 else
135 return "";
136 }
137
Eject(const std::string & mountpath)138 bool CStorageProvider::Eject(const std::string& mountpath)
139 {
140 return false;
141 }
142
GetDiskUsage()143 std::vector<std::string> CStorageProvider::GetDiskUsage()
144 {
145 using KODI::PLATFORM::WINDOWS::FromW;
146
147 std::vector<std::string> result;
148 ULARGE_INTEGER ULTotal = { { 0 } };
149 ULARGE_INTEGER ULTotalFree = { { 0 } };
150 std::string strRet;
151
152 auto localfolder = ApplicationData::Current().LocalFolder().Path();
153 GetDiskFreeSpaceExW(localfolder.c_str(), nullptr, &ULTotal, &ULTotalFree);
154 strRet = StringUtils::Format("%s: %d MB %s", g_localizeStrings.Get(21440),
155 (ULTotalFree.QuadPart / (1024 * 1024)),
156 g_localizeStrings.Get(160).c_str());
157 result.push_back(strRet);
158
159 DWORD drivesBits = GetLogicalDrives();
160 if (drivesBits == 0)
161 return result;
162
163 CMediaSource share;
164
165 drivesBits >>= 2; // skip A and B
166 char driveLetter = 'C'; // start with C
167 for (; drivesBits > 0; drivesBits >>= 1, driveLetter++)
168 {
169 if (!(drivesBits & 1))
170 continue;
171
172 std::string strDrive = std::string(1, driveLetter) + ":\\";
173 if (DRIVE_FIXED == GetDriveTypeA(strDrive.c_str())
174 && GetDiskFreeSpaceExA((strDrive.c_str()), nullptr, &ULTotal, &ULTotalFree))
175 {
176 strRet = StringUtils::Format("%s %d MB %s", strDrive.c_str(), int(ULTotalFree.QuadPart / (1024 * 1024)), g_localizeStrings.Get(160).c_str());
177 result.push_back(strRet);
178 }
179 }
180 return result;
181 }
182
PumpDriveChangeEvents(IStorageEventsCallback * callback)183 bool CStorageProvider::PumpDriveChangeEvents(IStorageEventsCallback *callback)
184 {
185 bool res = m_changed.load();
186 m_changed = false;
187 return res;
188 }
189
GetDrivesByType(VECSOURCES & localDrives,Drive_Types eDriveType,bool bonlywithmedia)190 void CStorageProvider::GetDrivesByType(VECSOURCES & localDrives, Drive_Types eDriveType, bool bonlywithmedia)
191 {
192 DWORD drivesBits = GetLogicalDrives();
193 if (drivesBits == 0)
194 return;
195
196 CMediaSource share;
197 char volumeName[100];
198
199 drivesBits >>= 2; // skip A and B
200 char driveLetter = 'C'; // start with C
201 for (; drivesBits > 0; drivesBits >>= 1, driveLetter++)
202 {
203 if (!(drivesBits & 1))
204 continue;
205
206 std::string strDrive = std::string(1, driveLetter) + ":\\";
207 UINT uDriveType = GetDriveTypeA(strDrive.c_str());
208 int nResult = GetVolumeInformationA(strDrive.c_str(), volumeName, 100, 0, 0, 0, NULL, 25);
209 if (nResult == 0 && bonlywithmedia)
210 {
211 continue;
212 }
213
214 bool bUseDCD = false;
215 // skip unsupported types
216 if (uDriveType < DRIVE_REMOVABLE || uDriveType > DRIVE_CDROM)
217 continue;
218 // only fixed and remote
219 if (eDriveType == LOCAL_DRIVES && uDriveType != DRIVE_FIXED && uDriveType != DRIVE_REMOTE)
220 continue;
221 // only removable
222 if (eDriveType == REMOVABLE_DRIVES && uDriveType != DRIVE_REMOVABLE)
223 continue;
224 // only CD-ROMs
225 if (eDriveType == DVD_DRIVES && uDriveType != DRIVE_CDROM)
226 continue;
227
228 share.strPath = strDrive;
229 if (volumeName[0] != L'\0')
230 share.strName = volumeName;
231 if (uDriveType == DRIVE_CDROM && nResult)
232 {
233 // Has to be the same as auto mounted devices
234 share.strStatus = share.strName;
235 share.strName = share.strPath;
236 share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
237 bUseDCD = true;
238 }
239 else
240 {
241 // Lets show it, like Windows explorer do...
242 switch (uDriveType)
243 {
244 case DRIVE_CDROM:
245 share.strName = StringUtils::Format("%s (%s)", share.strPath.c_str(), g_localizeStrings.Get(218).c_str());
246 break;
247 case DRIVE_REMOVABLE:
248 if (share.strName.empty())
249 share.strName = StringUtils::Format("%s (%s)", g_localizeStrings.Get(437).c_str(), share.strPath.c_str());
250 break;
251 default:
252 if (share.strName.empty())
253 share.strName = share.strPath;
254 else
255 share.strName = StringUtils::Format("%s (%s)", share.strPath.c_str(), share.strName.c_str());
256 break;
257 }
258 }
259
260 StringUtils::Replace(share.strName, ":\\", ":");
261 StringUtils::Replace(share.strPath, ":\\", ":");
262 share.m_ignore = true;
263 if (!bUseDCD)
264 {
265 share.m_iDriveType = (
266 (uDriveType == DRIVE_FIXED) ? CMediaSource::SOURCE_TYPE_LOCAL :
267 (uDriveType == DRIVE_REMOTE) ? CMediaSource::SOURCE_TYPE_REMOTE :
268 (uDriveType == DRIVE_CDROM) ? CMediaSource::SOURCE_TYPE_DVD :
269 (uDriveType == DRIVE_REMOVABLE) ? CMediaSource::SOURCE_TYPE_REMOVABLE :
270 CMediaSource::SOURCE_TYPE_UNKNOWN);
271 }
272
273 AddOrReplace(localDrives, share);
274 }
275 }
276
277