ProjectArcade

Форк
0
535 строк · 18.8 Кб
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Text;
5
using System.IO;
6
using System.Diagnostics;
7
using System.Text.RegularExpressions;
8
using System.ComponentModel;
9

10
namespace EmulatorLauncher.Common.Compression
11
{
12
    public class Zip
13
    {
14
        public static bool IsCompressedFile(string fileName)
15
        {
16
            if (string.IsNullOrEmpty(fileName) || !File.Exists(fileName) || Directory.Exists(fileName))
17
                return false;
18

19
            var ext = Path.GetExtension(fileName).ToLowerInvariant();
20
            return ext == ".zip" || ext == ".7z" || ext == ".rar" || ext.Contains("squashfs");
21
        }
22

23
        public static bool IsSevenZipAvailable
24
        {
25
            get { return File.Exists(GetSevenZipPath()); }
26
        }
27

28
        public static string GetSevenZipPath()
29
        {
30
            string fn = Path.Combine(Path.GetDirectoryName(typeof(Zip).Assembly.Location), "7z.exe");
31
            if (File.Exists(fn))
32
                return fn;
33

34
            return Path.Combine(Path.GetDirectoryName(typeof(Zip).Assembly.Location), "7za.exe");
35
        }
36

37
        public static string GetRdSquashFSPath()
38
        {
39
            return Path.Combine(Path.GetDirectoryName(typeof(Zip).Assembly.Location), "rdsquashfs.exe");
40
        }
41
        
42
        public static bool IsFreeDiskSpaceAvailableForExtraction(string filename, string pathForExtraction)
43
        {
44
            try
45
            {
46
                var totalRequiredSize = Zip.ListEntries(filename).Sum(f => f.Length);
47
                if (totalRequiredSize == 0)
48
                    totalRequiredSize = new FileInfo(filename).Length;
49

50
                long freeSpaceOnDrive = new DriveInfo(Path.GetPathRoot(pathForExtraction)).AvailableFreeSpace;
51
                if (freeSpaceOnDrive < totalRequiredSize)
52
                    return false;
53
            }
54
            catch { }
55

56
            return true;
57
        }
58

59
        private static System.Reflection.MethodInfo _zipOpenRead;
60

61
        public static ZipEntry[] ListEntries(string path)
62
        {
63
            ZipEntry[] ret;
64
            if (!_entriesCache.TryGetValue(path, out ret))
65
            {
66
                ret = ListEntriesInternal(path);
67
                _entriesCache[path] = ret;
68
            }
69

70
            return ret;
71
        }
72

73
        // Dotnet 4.0 compatible Zip entries reader ( ZipFile exists since 4.5 )
74
        private static ZipEntry[] ListEntriesInternal(string path)
75
        {
76
            if (!IsCompressedFile(path))
77
                return new ZipEntry[] { };
78

79
            var ext = Path.GetExtension(path).ToLowerInvariant();
80

81
            if (ext.Contains("squashfs") && File.Exists(GetRdSquashFSPath()))
82
                return GetSquashFsEntries(path);
83

84
            if (ext != ".zip")
85
                return GetSevenZipEntries(path);
86

87
            IDisposable zipArchive = null;
88

89
            try
90
            {
91
                if (_zipOpenRead == null)
92
                {
93
                    var afs = System.Reflection.Assembly.Load("System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
94
                    if (afs == null)
95
                        return GetSevenZipEntries(path);
96

97
                    var ass = System.Reflection.Assembly.Load("System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
98
                    if (ass == null)
99
                        return GetSevenZipEntries(path);
100

101
                    var zipFile = afs.GetTypes().FirstOrDefault(t => t.Name == "ZipFile");
102
                    if (zipFile == null)
103
                        return GetSevenZipEntries(path);
104

105
                    _zipOpenRead = zipFile.GetMember("OpenRead", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public).FirstOrDefault() as System.Reflection.MethodInfo;
106
                    if (_zipOpenRead == null)
107
                        return GetSevenZipEntries(path);
108
                }
109
                             
110
                zipArchive = _zipOpenRead.Invoke(null, new object[] { path }) as IDisposable;
111
                if (zipArchive == null)
112
                    return new ZipEntry[] { };
113

114
                List<ZipEntry> ret = new List<ZipEntry>();
115

116
                var entries = zipArchive.GetType().GetValue<System.Collections.IEnumerable>(zipArchive, "Entries");
117

118
                foreach (var entry in entries)
119
                {
120
                    var zipArchiveEntry = entry.GetType();
121

122
                    string fullName = zipArchiveEntry.GetValue<string>(entry, "FullName");
123
                    if (!string.IsNullOrEmpty(fullName))
124
                    {
125
                        ZipEntry e = new ZipEntry();
126
                        e.Filename = fullName;
127
                        e.Length = zipArchiveEntry.GetValue<long>(entry, "Length");
128
                        e.LastModified = zipArchiveEntry.GetValue<DateTimeOffset>(entry, "LastWriteTime").DateTime;
129

130
                        if (fullName.EndsWith("/"))
131
                        {
132
                            e.Filename = e.Filename.Substring(0, e.Filename.Length - 1);
133
                            e.IsDirectory = true;
134
                        }
135

136
                        ret.Add(e);
137
                    }
138
                }
139

140
                return ret.ToArray();
141
            }
142
            catch
143
            {
144
                return GetSevenZipEntries(path);
145
            }
146
            finally
147
            {
148
                if (zipArchive != null)
149
                    zipArchive.Dispose();               
150
            }
151
        }
152

153
        public class SquashFsEntry : ZipEntry
154
        {
155
            private string _arch;
156

157
            public SquashFsEntry(string arch)
158
            {
159
                _arch = arch;
160
                base.Length = -1;
161
            }
162

163
            public override long Length
164
            {
165
                get
166
                {
167
                    if (base.Length == -1)
168
                    {
169
                        if (IsDirectory)
170
                            base.Length = 0;
171
                        else
172
                        {
173
                            string lineOutput = ProcessExtensions.RunWithOutput(GetRdSquashFSPath(), "-s \"" + Filename + "\" \"" + _arch + "\"");
174

175
                            var fs = lineOutput.ExtractString("File size: ", "\r");
176

177
                            long len;
178
                            if (long.TryParse(fs, out len))
179
                                base.Length = len;
180
                            else
181
                                base.Length = 0;
182
                        }
183
                    }
184

185
                    return base.Length;
186
                }
187
                set
188
                {
189
                    base.Length = value;
190
                }
191
            }
192
        }
193

194
        private static Dictionary<string, ZipEntry[]> _entriesCache = new Dictionary<string, ZipEntry[]>();
195

196
        private static ZipEntry[] GetSquashFsEntries(string archive)
197
        {
198
            ZipEntry[] ret;
199
            if (!_entriesCache.TryGetValue(archive, out ret))
200
            {
201
                ret = GetSquashFsEntriesInternal(archive);
202
                _entriesCache[archive] = ret;
203
            }
204
             
205
            return ret;
206
        }
207

208
        private static ZipEntry[] GetSquashFsEntriesInternal(string archive)
209
        {
210
            var sevenZip = GetRdSquashFSPath();
211
            if (!File.Exists(sevenZip))
212
                return new ZipEntry[] { };
213

214
            string output = ProcessExtensions.RunWithOutput(GetRdSquashFSPath(), "-d \"" + archive + "\"");
215
            if (output == null)
216
                return new ZipEntry[] { };
217

218
            List<ZipEntry> ret = new List<ZipEntry>();
219

220
            foreach (string str in output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
221
            {
222
                var args = str.SplitCommandLine();
223
                if (args.Length < 5)
224
                    continue;
225

226
                if (args[0] != "file" && args[0] != "dir")
227
                    continue;
228

229
                ZipEntry e = new SquashFsEntry(archive);
230
                e.Filename = args[1];
231
                e.IsDirectory = args[0] == "dir";
232
                if (e.IsDirectory)
233
                    e.Length = 0;
234

235
                if (args.Length >= 6)
236
                {
237
                    long lastModifiedSpan;
238
                    if (long.TryParse(args[5], out lastModifiedSpan))
239
                    {
240
                        var dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
241
                        dt = dt.AddSeconds(lastModifiedSpan);
242
                        e.LastModified = dt;
243
                    }
244

245
                    e.Length = 0;
246
                }
247

248
                if (args.Length >= 7)
249
                {
250
                    long len;
251
                    if (long.TryParse(args[6], out len))
252
                        e.Length = len;
253
                }
254

255
                ret.Add(e);
256
            }
257

258
            return ret.ToArray();
259
        }
260

261
        private static Regex _listArchiveRegex = new Regex(@"^(\d{2,4}-\d{2,4}-\d{2,4})\s+(\d{2}:\d{2}:\d{2})\s+(.{5})\s+(\d+)\s+(\d+)?\s+(.+)");
262

263
        private static ZipEntry[] GetSevenZipEntries(string archive)
264
        {
265
            var sevenZip = GetSevenZipPath();
266
            if (!File.Exists(sevenZip))
267
                return new ZipEntry[] { };
268

269
            string output = ProcessExtensions.RunWithOutput(GetSevenZipPath(), "l \"" + archive + "\"");
270
            if (output == null)
271
                return new ZipEntry[] { };
272

273
            int num = 0;
274

275
            List<ZipEntry> ret = new List<ZipEntry>();
276

277
            foreach (string str in output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
278
            {
279
                if (str.StartsWith("---"))
280
                    num++;
281
                else if (_listArchiveRegex.IsMatch(str) && num == 1)
282
                {
283
                    var matches = _listArchiveRegex.Matches(str);
284

285
                    List<string> groups = matches[0]
286
                        .Groups.Cast<Group>()
287
                        .Select(x => x.Value)
288
                        .ToList();
289

290
                    if (groups.Count == 7)
291
                    {
292
                        ZipEntry e = new ZipEntry();
293
                        e.Filename = groups[6];
294
                        e.IsDirectory = groups[3].Contains("D");
295

296
                        DateTime date;
297
                        if (DateTime.TryParse(groups[1] + " " + groups[2], out date))
298
                            e.LastModified = date;
299

300
                        long len;
301
                        if (long.TryParse(groups[4], out len))
302
                            e.Length = len;
303

304
                        ret.Add(e);
305
                    }
306
                }
307
            }
308

309
            return ret.ToArray();
310
        }
311
   
312
        static void ExtractSquashFS(string archive, string destination, string fileNameToExtract = null, ProgressChangedEventHandler progress = null)
313
        {
314
            var rdsquashfs = GetRdSquashFSPath();
315
            if (!File.Exists(rdsquashfs))
316
                return;
317

318
            HashSet<string> entries = null;
319

320
            if (progress != null && fileNameToExtract == null)
321
            {
322
                entries = new HashSet<string>(GetSquashFsEntries(archive).Where(e => !e.IsDirectory).Select(e => e.Filename));
323
                if (entries == null || entries.Count == 0)
324
                    return;
325
            }
326

327
            if (fileNameToExtract == null)
328
            {
329
                try { Directory.Delete(destination, true); }
330
                catch { }
331

332
                Directory.CreateDirectory(destination);
333
            }
334
            else
335
            {
336
                destination = Path.GetDirectoryName(Path.Combine(destination, fileNameToExtract));
337
                if (!Directory.Exists(destination))
338
                    Directory.CreateDirectory(destination);
339
            }
340

341
            string args = "--no-dev --no-sock --no-fifo --no-slink -u /. \"" + archive + "\"";
342
            if (!string.IsNullOrEmpty(fileNameToExtract))
343
                args = "--no-dev --no-sock --no-fifo --no-slink -u \"" + fileNameToExtract.Replace("\\", "/") + "\" \"" + archive + "\"";
344

345
            if (progress == null)
346
                args = "-q " + args;
347

348
            var px = new ProcessStartInfo()
349
            {
350
                FileName = rdsquashfs,
351
                WorkingDirectory = destination,
352
                Arguments = args,                
353
                UseShellExecute = false,
354
                RedirectStandardOutput = (progress != null),                
355
                CreateNoWindow = true
356
            };            
357

358
            var proc = Process.Start(px);
359
            if (proc != null && progress != null && entries != null)
360
            {
361
                var unpacking = new HashSet<string>(entries);
362

363
                int totalEntries = entries.Count; // *2;
364
                int lastpc = 0;
365

366
                try
367
                {
368
                    while (!proc.StandardOutput.EndOfStream)
369
                    {
370
                        string line = proc.StandardOutput.ReadLine();
371
                        Debug.WriteLine(line);
372

373
                        if (line.StartsWith("creating "))
374
                        {
375
                            line = line.Substring("creating ".Length).Trim();
376
                            entries.Remove(line);
377
                        }
378

379
                        if (line.StartsWith("unpacking "))
380
                        {
381
                            line = line.Substring("unpacking ".Length).Trim();
382
                            unpacking.Remove(line);
383
                        }
384

385
                        int pc = ((totalEntries - unpacking.Count) * 100) / totalEntries;
386
                        if (pc != lastpc)
387
                        {
388
                            lastpc = pc;
389
                            progress(null, new ProgressChangedEventArgs(pc, null));
390
                        }
391
                    }
392
                }
393
                catch { }
394
            }
395

396
            proc.WaitForExit();
397

398
            int code = proc.ExitCode;
399
            if (code == 2)
400
                throw new ApplicationException("Cannot open archive");
401
        }
402

403
        public static void Extract(string archive, string destination, string fileNameToExtract = null, ProgressChangedEventHandler progress = null, bool keepFolder = false)
404
        {
405
            var ext = Path.GetExtension(archive).ToLowerInvariant();
406
            if (ext.Contains("squashfs") && File.Exists(GetRdSquashFSPath()))
407
            {
408
                ExtractSquashFS(archive, destination, fileNameToExtract, progress);
409
                return;
410
            }
411

412
            var sevenZip = GetSevenZipPath();
413
            if (!File.Exists(sevenZip))
414
                return;
415

416
            string args = "x -bsp1 \"" + archive + "\" -y -o\"" + destination + "\"";
417
            if (!string.IsNullOrEmpty(fileNameToExtract))
418
            {
419
                // Multiple files :
420
                // H:\[Emulz]\[batocera-ports]\7z x -bsp1 "h:\\applewin.7z" "History.txt" "MASTER.DSK" -y -o"h:\\tmp\\"
421

422
                if (keepFolder)
423
                    args = "x -bsp1 \"" + archive + "\" \"" + fileNameToExtract + "\" -y -o\"" + destination + "\"";
424
                else
425
                    args = "e -bsp1 \"" + archive + "\" \"" + fileNameToExtract + "\" -y -o\"" + destination + "\"";
426
            }
427

428
            var px = new ProcessStartInfo()
429
            {
430
                FileName = GetSevenZipPath(),
431
                WorkingDirectory = Path.GetDirectoryName(GetSevenZipPath()),
432
                Arguments = args,
433
                UseShellExecute = false,
434
                RedirectStandardOutput = (progress != null),
435
                CreateNoWindow = true
436
            };
437

438
            var proc = Process.Start(px);
439

440
            if (proc != null && progress != null)
441
            {
442
                try
443
                {
444
                    StringBuilder sbOutput = new StringBuilder();
445

446
                    while (!proc.StandardOutput.EndOfStream)
447
                    {
448
                        string line = proc.StandardOutput.ReadLine();
449
                        if (line.Length >= 4 && line[3] == '%')
450
                        {
451
                            int pc;
452
                            if (int.TryParse(line.Substring(0, 3), out pc))
453
                                progress(null, new ProgressChangedEventArgs(pc, null));
454
                        }
455

456
                        sbOutput.AppendLine(line);
457
                    }
458
                }
459
                catch { }
460
            }
461

462
            proc.WaitForExit();
463

464
            int code = proc.ExitCode;
465
            if (code == 2)
466
                throw new ApplicationException("Cannot open archive");
467
        }
468

469

470
        public static void CleanupUncompressedWSquashFS(string zipFile, string uncompressedPath)
471
        {
472
            if (Path.GetExtension(zipFile).ToLowerInvariant() != ".wsquashfs")
473
                return;
474

475
            string[] pathsToDelete = new string[]
476
                {
477
                    "dosdevices",
478
                    "system.reg",
479
                    "userdef.reg",
480
                    "user.reg",
481
                    ".update-timestamp",
482
                    "drive_c\\windows",
483
                    "drive_c\\Program Files\\Common Files\\System",
484
                    "drive_c\\Program Files\\Common Files\\Microsoft Shared",
485
                    "drive_c\\Program Files\\Internet Explorer",
486
                    "drive_c\\Program Files\\Windows Media Player",
487
                    "drive_c\\Program Files\\Windows NT",
488
                    "drive_c\\Program Files (x86)\\Common Files\\System",
489
                    "drive_c\\Program Files (x86)\\Common Files\\Microsoft Shared",
490
                    "drive_c\\Program Files (x86)\\Internet Explorer",
491
                    "drive_c\\Program Files (x86)\\Windows Media Player",
492
                    "drive_c\\Program Files (x86)\\Windows NT",
493
                    "drive_c\\users\\Public",
494
                    "drive_c\\ProgramData\\Microsoft"
495
                };
496

497
            foreach (var path in pathsToDelete)
498
            {
499
                string folder = Path.Combine(uncompressedPath, path);
500
                if (Directory.Exists(folder))
501
                {
502
                    try { Directory.Delete(folder, true); }
503
                    catch { }
504
                }
505
                else if (File.Exists(folder))
506
                {
507
                    try { File.Delete(folder); }
508
                    catch { }
509
                }
510

511
                try
512
                {
513
                    var parent = Path.GetDirectoryName(folder);
514
                    if (Directory.Exists(parent))
515
                        Directory.Delete(parent);
516
                }
517
                catch { }
518
            }
519
        }
520
    }
521

522
    public class ZipEntry
523
    {
524
        public string Filename { get; set; }
525
        public bool IsDirectory { get; set; }
526
        
527
        virtual public long Length { get; set; }
528
        virtual public DateTime LastModified { get; set; }
529
 
530
        public override string ToString()
531
        {
532
            return Filename;
533
        }
534
    }    
535
}
536

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.