Client LuaCsForBarotrauma
SafeIO.cs
1 #nullable enable
2 
3 using System;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
6 using System.Linq;
7 #if CLIENT
9 using Barotrauma.Steam;
10 #endif
11 
12 namespace Barotrauma.IO
13 {
14  static class Validation
15  {
16  private static readonly ImmutableArray<Identifier> unwritableDirs = new[] { "Content".ToIdentifier() }.ToImmutableArray();
17  private static readonly ImmutableArray<Identifier> unwritableExtensions = new[]
18  {
19  ".exe", ".dll", ".json", ".pdb", ".com", ".scr", ".dylib", ".so", ".a", ".app", //executables and libraries
20  ".bat", ".sh", //shell scripts
21  }.ToIdentifiers().ToImmutableArray();
22 
23  public ref struct Skipper
24  {
25  public void Dispose()
26  {
27  SkipValidationInDebugBuilds = false;
28  }
29  }
30 
34  public static Skipper SkipInDebugBuilds()
35  {
36  SkipValidationInDebugBuilds = true;
37  return new Skipper();
38  }
39 
43  public static bool SkipValidationInDebugBuilds;
44 
45  public static bool CanWrite(string path, bool isDirectory)
46  {
47  string getFullPath(string p)
48  => System.IO.Path.GetFullPath(p).CleanUpPath();
49 
50  path = getFullPath(path);
51  string localModsDir = getFullPath(ContentPackage.LocalModsDir);
52  string workshopModsDir = getFullPath(ContentPackage.WorkshopModsDir);
53 #if CLIENT
54  string workshopStagingDir = getFullPath(SteamManager.Workshop.PublishStagingDir);
55  string tempDownloadDir = getFullPath(ModReceiver.DownloadFolder);
56 #endif
57 
58  if (!isDirectory)
59  {
60  Identifier extension = System.IO.Path.GetExtension(path).Replace(" ", "").ToIdentifier();
61 
62  bool pathStartsWith(string prefix)
63  => path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
64 
65  if (!pathStartsWith(workshopModsDir)
66  && !pathStartsWith(localModsDir)
67 #if CLIENT
68  && !pathStartsWith(tempDownloadDir)
69  && !pathStartsWith(workshopStagingDir)
70 #endif
71  && unwritableExtensions.Any(e => e == extension))
72  {
73  return false;
74  }
75  }
76 
77  foreach (var unwritableDir in unwritableDirs)
78  {
79  string dir = System.IO.Path.GetFullPath(unwritableDir.Value).CleanUpPath();
80 
81  if (path.StartsWith(dir, StringComparison.InvariantCultureIgnoreCase))
82  {
83 #if DEBUG
84  return SkipValidationInDebugBuilds;
85 #else
86  return false;
87 #endif
88  }
89  }
90 
91  return true;
92  }
93  }
94 
95  public static class SafeXML
96  {
97  public static void SaveSafe(
98  this System.Xml.Linq.XDocument doc,
99  string path,
100  System.Xml.Linq.SaveOptions saveOptions = System.Xml.Linq.SaveOptions.None,
101  bool throwExceptions = false)
102  {
103  if (!Validation.CanWrite(path, false))
104  {
105  string errorMsg = $"Cannot save XML document to \"{path}\": modifying the files in this folder/with this extension is not allowed.";
106  if (throwExceptions)
107  {
108  throw new InvalidOperationException(errorMsg);
109  }
110  else
111  {
112  DebugConsole.ThrowError(errorMsg);
113  }
114  return;
115  }
116  doc.Save(path, saveOptions);
117  }
118 
119  public static void SaveSafe(this System.Xml.Linq.XElement element, string path, bool throwExceptions = false)
120  {
121  if (!Validation.CanWrite(path, false))
122  {
123  string errorMsg = $"Cannot save XML element to \"{path}\": modifying the files in this folder/with this extension is not allowed.";
124  if (throwExceptions)
125  {
126  throw new InvalidOperationException(errorMsg);
127  }
128  else
129  {
130  DebugConsole.ThrowError(errorMsg);
131  }
132  return;
133  }
134  element.Save(path);
135  }
136 
137  public static void SaveSafe(this System.Xml.Linq.XDocument doc, XmlWriter writer)
138  {
139  doc.WriteTo(writer);
140  }
141 
142  public static void WriteTo(this System.Xml.Linq.XDocument doc, XmlWriter writer)
143  {
144  writer.Write(doc);
145  }
146  }
147 
148  public class XmlWriter : IDisposable
149  {
150  public readonly System.Xml.XmlWriter? Writer;
151 
152  public XmlWriter(string path, System.Xml.XmlWriterSettings settings)
153  {
154  if (!Validation.CanWrite(path, false))
155  {
156  DebugConsole.ThrowError($"Cannot write XML document to \"{path}\": modifying the files in this folder/with this extension is not allowed.");
157  Writer = null;
158  return;
159  }
160  Writer = System.Xml.XmlWriter.Create(path, settings);
161  }
162 
163  public static XmlWriter Create(string path, System.Xml.XmlWriterSettings settings)
164  {
165  return new XmlWriter(path, settings);
166  }
167 
168  public void Write(System.Xml.Linq.XDocument doc)
169  {
170  if (Writer == null)
171  {
172  DebugConsole.ThrowError("Cannot write to invalid XmlWriter");
173  return;
174  }
175  doc.WriteTo(Writer);
176  }
177 
178  public void Flush()
179  {
180  if (Writer == null)
181  {
182  DebugConsole.ThrowError("Cannot flush invalid XmlWriter");
183  return;
184  }
185  Writer.Flush();
186  }
187 
188  public void Dispose()
189  {
190  if (Writer == null)
191  {
192  DebugConsole.ThrowError("Cannot dispose invalid XmlWriter");
193  return;
194  }
195  Writer.Dispose();
196  }
197  }
198 
199  public static class XmlWriterExtensions
200  {
201  public static void Save(this System.Xml.Linq.XDocument doc, XmlWriter writer)
202  {
203  doc.Save(writer.Writer ?? throw new NullReferenceException("Unable to save XML document: XML writer is null."));
204  }
205  }
206 
207  public static class Path
208  {
209  public static readonly char DirectorySeparatorChar = System.IO.Path.DirectorySeparatorChar;
210  public static readonly char AltDirectorySeparatorChar = System.IO.Path.AltDirectorySeparatorChar;
211 
212  public static string GetExtension(string path) => System.IO.Path.GetExtension(path);
213 
214  public static string GetFileNameWithoutExtension(string path) => System.IO.Path.GetFileNameWithoutExtension(path);
215 
216  public static string? GetPathRoot(string? path) => System.IO.Path.GetPathRoot(path);
217 
218  public static string GetRelativePath(string relativeTo, string path) => System.IO.Path.GetRelativePath(relativeTo, path);
219 
220  public static string GetDirectoryName(ContentPath path) => GetDirectoryName(path.Value)!;
221 
222  public static string? GetDirectoryName(string path) => System.IO.Path.GetDirectoryName(path);
223 
224  public static string GetFileName(string path) => System.IO.Path.GetFileName(path);
225 
226  public static string GetFullPath(string path) => System.IO.Path.GetFullPath(path);
227 
228  public static string Combine(params string[] s) => System.IO.Path.Combine(s);
229 
230  public static string GetTempFileName() => System.IO.Path.GetTempFileName();
231 
232  public static bool IsPathRooted(string path) => System.IO.Path.IsPathRooted(path);
233 
234  private static readonly ImmutableHashSet<char> invalidFileNameChars = ImmutableHashSet.Create
235  (
236  '\"', '<', '>', '|', '\0',
237  (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
238  (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
239  (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
240  (char)31, ':', '*', '?', '\\', '/'
241  );
242 
246  public static ImmutableHashSet<char> GetInvalidFileNameCharsCrossPlatform() => invalidFileNameChars;
247  }
248 
249  public static class Directory
250  {
251  public static string GetCurrentDirectory()
252  {
253  // Intentionally crash with all exceptions, if this fails.
254  return System.IO.Directory.GetCurrentDirectory();
255  }
256 
257  public static void SetCurrentDirectory(string path)
258  {
259  // Intentionally crash with all exceptions, if this fails.
260  System.IO.Directory.SetCurrentDirectory(path);
261  }
262 
263  public static string[] GetFiles(string path)
264  {
265  try
266  {
267  return System.IO.Directory.GetFiles(path);
268  }
269  catch (UnauthorizedAccessException e)
270  {
271  DebugConsole.ThrowError($"Cannot get files at \"{path}\": unauthorized access. The folder/file(s) might be read-only!", e);
272  return Array.Empty<string>();
273  }
274  }
275 
276  public static string[] GetFiles(string path, string pattern, System.IO.SearchOption option = System.IO.SearchOption.AllDirectories)
277  {
278  try
279  {
280  return System.IO.Directory.GetFiles(path, pattern, option);
281  }
282  catch (UnauthorizedAccessException e)
283  {
284  DebugConsole.ThrowError($"Cannot get files at \"{path}\": unauthorized access. The folder/file(s) might be read-only!", e);
285  return Array.Empty<string>();
286  }
287  }
288 
289  public static string[] GetDirectories(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly)
290  {
291  try
292  {
293  return System.IO.Directory.GetDirectories(path, searchPattern, searchOption);
294  }
295  catch (UnauthorizedAccessException e)
296  {
297  DebugConsole.ThrowError($"Cannot get directories at \"{path}\": unauthorized access. The folder(s) might be read-only!", e);
298  return Array.Empty<string>();
299  }
300  }
301 
302  public static string[] GetFileSystemEntries(string path)
303  {
304  try
305  {
306  return System.IO.Directory.GetFileSystemEntries(path);
307  }
308  catch (UnauthorizedAccessException e)
309  {
310  DebugConsole.ThrowError($"Cannot get file system entries at \"{path}\": unauthorized access. The file/folder might be read-only!", e);
311  return Array.Empty<string>();
312  }
313  }
314 
315  public static IEnumerable<string> EnumerateDirectories(string path, string pattern)
316  {
317  try
318  {
319  return System.IO.Directory.EnumerateDirectories(path, pattern);
320  }
321  catch (UnauthorizedAccessException e)
322  {
323  DebugConsole.ThrowError($"Cannot enumerate directories at \"{path}\": unauthorized access. The folder(s) might be read-only!", e);
324  return Array.Empty<string>();
325  }
326  }
327 
328  public static IEnumerable<string> EnumerateFiles(string path, string pattern)
329  {
330  try
331  {
332  return System.IO.Directory.EnumerateFiles(path, pattern);
333  }
334  catch (UnauthorizedAccessException e)
335  {
336  DebugConsole.ThrowError($"Cannot enumerate files at \"{path}\": unauthorized access. The file(s)/folder(s) might be read-only!", e);
337  return Array.Empty<string>();
338  }
339  }
340 
341  public static bool Exists(string path)
342  {
343  return System.IO.Directory.Exists(path);
344  }
345 
346  public static System.IO.DirectoryInfo? CreateDirectory(string path)
347  {
348  if (!Validation.CanWrite(path, true))
349  {
350  DebugConsole.ThrowError($"Cannot create directory \"{path}\": modifying the contents of this folder/using this extension is not allowed.");
351  Validation.CanWrite(path, true);
352  return null;
353  }
354  try
355  {
356  return System.IO.Directory.CreateDirectory(path);
357  }
358  catch (UnauthorizedAccessException e)
359  {
360  DebugConsole.ThrowError($"Cannot create directory at \"{path}\": unauthorized access. The file/folder might be read-only!", e);
361  return null;
362  }
363  }
364 
365  public static void Delete(string path, bool recursive=true)
366  {
367  if (!Validation.CanWrite(path, true))
368  {
369  DebugConsole.ThrowError($"Cannot delete directory \"{path}\": modifying the contents of this folder/using this extension is not allowed.");
370  return;
371  }
372  //TODO: validate recursion?
373  try
374  {
375  System.IO.Directory.Delete(path, recursive);
376  }
377  catch (UnauthorizedAccessException e)
378  {
379  DebugConsole.ThrowError($"Cannot delete \"{path}\": unauthorized access. The file/folder might be read-only!", e);
380  }
381  }
382 
383  public static bool TryDelete(string path, bool recursive = true)
384  {
385  try
386  {
387  Delete(path, recursive);
388  return true;
389  }
390  catch
391  {
392  return false;
393  }
394  }
395 
396  public static DateTime GetLastWriteTime(string path)
397  {
398  try
399  {
400  return System.IO.Directory.GetLastWriteTime(path);
401  }
402  catch (UnauthorizedAccessException e)
403  {
404  DebugConsole.ThrowError($"Cannot get last write time at \"{path}\": unauthorized access. The file/folder might be read-only!", e);
405  return new DateTime();
406  }
407  }
408  }
409 
410  public static class File
411  {
412  public static bool Exists(ContentPath path) => Exists(path.Value);
413 
414  public static bool Exists(string path) => System.IO.File.Exists(path);
415 
416  public static void Copy(string src, string dest, bool overwrite = false)
417  {
418  if (!Validation.CanWrite(dest, false))
419  {
420  DebugConsole.ThrowError($"Cannot copy \"{src}\" to \"{dest}\": modifying the contents of this folder/using this extension is not allowed.");
421  return;
422  }
423  try
424  {
425  System.IO.File.Copy(src, dest, overwrite);
426  }
427  catch (UnauthorizedAccessException e)
428  {
429  DebugConsole.ThrowError($"Cannot copy \"{src}\" to \"{dest}\": unauthorized access. The file/folder might be read-only!", e);
430  }
431  }
432 
433  public static void Move(string src, string dest)
434  {
435  if (!Validation.CanWrite(src, false))
436  {
437  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": modifying the contents of the source folder is not allowed.");
438  return;
439  }
440  if (!Validation.CanWrite(dest, false))
441  {
442  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": modifying the contents of the destination folder is not allowed");
443  return;
444  }
445  try
446  {
447  System.IO.File.Move(src, dest);
448  }
449  catch (UnauthorizedAccessException e)
450  {
451  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": unauthorized access. The file/folder might be read-only!", e);
452  }
453  }
454 
455  public static void Delete(ContentPath path) => Delete(path.Value);
456 
457  public static void Delete(string path)
458  {
459  if (!Validation.CanWrite(path, false))
460  {
461  DebugConsole.ThrowError($"Cannot delete file \"{path}\": modifying the contents of this folder/using this extension is not allowed.");
462  return;
463  }
464  try
465  {
466  System.IO.File.Delete(path);
467  }
468  catch (UnauthorizedAccessException e)
469  {
470  DebugConsole.ThrowError($"Cannot delete {path}: unauthorized access. The file/folder might be read-only!", e);
471  }
472  }
473 
474  public static DateTime GetLastWriteTime(string path)
475  {
476  return System.IO.File.GetLastWriteTime(path);
477  }
478 
479  public static FileStream? Open(
480  string path,
481  System.IO.FileMode mode,
482  System.IO.FileAccess access = System.IO.FileAccess.ReadWrite,
483  System.IO.FileShare? share = null)
484  {
485  switch (mode)
486  {
487  case System.IO.FileMode.Create:
488  case System.IO.FileMode.CreateNew:
489  case System.IO.FileMode.OpenOrCreate:
490  case System.IO.FileMode.Append:
491  case System.IO.FileMode.Truncate:
492  if (!Validation.CanWrite(path, false))
493  {
494  DebugConsole.ThrowError($"Cannot open \"{path}\" in {mode} mode: modifying the contents of this folder/using this extension is not allowed.");
495  return null;
496  }
497  break;
498  }
499  access =
500  !Validation.CanWrite(path, false) ?
501  System.IO.FileAccess.Read :
502  access;
503  var shareVal = share ?? (access == System.IO.FileAccess.Read ? System.IO.FileShare.Read : System.IO.FileShare.None);
504  try
505  {
506  return new FileStream(path, System.IO.File.Open(path, mode, access, shareVal));
507  }
508  catch (UnauthorizedAccessException e)
509  {
510  DebugConsole.ThrowError($"Cannot open {path} (stream): unauthorized access. The file/folder might be read-only!", e);
511  return null;
512  }
513  }
514 
515  public static FileStream? OpenRead(string path)
516  {
517  return Open(path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
518  }
519 
520  public static FileStream? OpenWrite(string path)
521  {
522  return Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write);
523  }
524 
525  public static FileStream? Create(string path)
526  {
527  return Open(path, System.IO.FileMode.Create, System.IO.FileAccess.Write);
528  }
529 
530  public static void WriteAllBytes(string path, byte[] contents)
531  {
532  if (!Validation.CanWrite(path, false))
533  {
534  DebugConsole.ThrowError($"Cannot write all bytes to \"{path}\": modifying the files in this folder/with this extension is not allowed.");
535  return;
536  }
537  try
538  {
539  System.IO.File.WriteAllBytes(path, contents);
540  }
541  catch (UnauthorizedAccessException e)
542  {
543  DebugConsole.ThrowError($"Cannot write at {path}: unauthorized access. The file/folder might be read-only!", e);
544  }
545  }
546 
547  public static void WriteAllText(string path, string contents, System.Text.Encoding? encoding = null)
548  {
549  if (!Validation.CanWrite(path, false))
550  {
551  DebugConsole.ThrowError($"Cannot write all text to \"{path}\": modifying the files in this folder/with this extension is not allowed.");
552  return;
553  }
554  try
555  {
556  System.IO.File.WriteAllText(path, contents, encoding ?? System.Text.Encoding.UTF8);
557  }
558  catch (UnauthorizedAccessException e)
559  {
560  DebugConsole.ThrowError($"Cannot write at {path}: unauthorized access. The file/folder might be read-only!", e);
561  }
562  }
563 
564  public static void WriteAllLines(string path, IEnumerable<string> contents, System.Text.Encoding? encoding = null)
565  {
566  if (!Validation.CanWrite(path, false))
567  {
568  DebugConsole.ThrowError($"Cannot write all lines to \"{path}\": modifying the files in this folder/with this extension is not allowed.");
569  return;
570  }
571  try
572  {
573  System.IO.File.WriteAllLines(path, contents, encoding ?? System.Text.Encoding.UTF8);
574  }
575  catch (UnauthorizedAccessException e)
576  {
577  DebugConsole.ThrowError($"Cannot write at {path}: unauthorized access. The file/folder might be read-only!", e);
578  }
579  }
580 
581  public static byte[] ReadAllBytes(string path)
582  {
583  try
584  {
585  return System.IO.File.ReadAllBytes(path);
586  }
587  catch (UnauthorizedAccessException e)
588  {
589  DebugConsole.ThrowError($"Cannot read {path}: unauthorized access. The file/folder might be read-only!", e);
590  return Array.Empty<byte>();
591  }
592  }
593 
594  public static string ReadAllText(string path, System.Text.Encoding? encoding = null)
595  {
596  try
597  {
598  return System.IO.File.ReadAllText(path, encoding ?? System.Text.Encoding.UTF8);
599  }
600  catch (UnauthorizedAccessException e)
601  {
602  DebugConsole.ThrowError($"Cannot read {path}: unauthorized access. The file/folder might be read-only!", e);
603  return string.Empty;
604  }
605  }
606 
607  public static string[] ReadAllLines(string path, System.Text.Encoding? encoding = null)
608  {
609  try
610  {
611  return System.IO.File.ReadAllLines(path, encoding ?? System.Text.Encoding.UTF8);
612  }
613  catch (UnauthorizedAccessException e)
614  {
615  DebugConsole.ThrowError($"Cannot read {path}: unauthorized access. The file/folder might be read-only!", e);
616  return Array.Empty<string>();
617  }
618  }
619  }
620 
621  public class FileStream : System.IO.Stream
622  {
623  private readonly System.IO.FileStream innerStream;
624  private readonly string fileName;
625 
626  public FileStream(string fn, System.IO.FileStream stream)
627  {
628  innerStream = stream;
629  fileName = fn;
630  }
631 
632  public override bool CanRead => innerStream.CanRead;
633  public override bool CanSeek => innerStream.CanSeek;
634  public override bool CanTimeout => innerStream.CanTimeout;
635  public override bool CanWrite
636  {
637  get
638  {
639  if (!Validation.CanWrite(fileName, false)) { return false; }
640  return innerStream.CanWrite;
641  }
642  }
643 
644  public override long Length => innerStream.Length;
645 
646  public override long Position
647  {
648  get
649  {
650  return innerStream.Position;
651  }
652  set
653  {
654  innerStream.Position = value;
655  }
656  }
657 
658  public override int Read(byte[] buffer, int offset, int count)
659  {
660  return innerStream.Read(buffer, offset, count);
661  }
662 
663  public override void Write(byte[] buffer, int offset, int count)
664  {
665  if (Validation.CanWrite(fileName, false))
666  {
667  innerStream.Write(buffer, offset, count);
668  }
669  else
670  {
671  DebugConsole.ThrowError($"Cannot write to file \"{fileName}\": modifying the files in this folder/with this extension is not allowed.");
672  }
673  }
674 
675  public override long Seek(long offset, System.IO.SeekOrigin origin)
676  {
677  return innerStream.Seek(offset, origin);
678  }
679 
680  public override void SetLength(long value)
681  {
682  innerStream.SetLength(value);
683  }
684 
685  public override void Flush()
686  {
687  innerStream.Flush();
688  }
689 
690  protected override void Dispose(bool notCalledByFinalizer)
691  {
692  if (notCalledByFinalizer) { innerStream.Dispose(); }
693  }
694  }
695 
696  public class DirectoryInfo
697  {
698  private System.IO.DirectoryInfo innerInfo;
699 
700  public DirectoryInfo(string path)
701  {
702  innerInfo = new System.IO.DirectoryInfo(path);
703  }
704 
705  private DirectoryInfo(System.IO.DirectoryInfo info)
706  {
707  innerInfo = info;
708  }
709 
710  public bool Exists => innerInfo.Exists;
711  public string Name => innerInfo.Name;
712  public string FullName => innerInfo.FullName;
713 
714  public System.IO.FileAttributes Attributes => innerInfo.Attributes;
715 
716  public IEnumerable<DirectoryInfo> GetDirectories()
717  {
718  var dirs = innerInfo.GetDirectories();
719  foreach (var dir in dirs)
720  {
721  yield return new DirectoryInfo(dir);
722  }
723  }
724 
725  public IEnumerable<FileInfo> GetFiles()
726  {
727  var files = innerInfo.GetFiles();
728  foreach (var file in files)
729  {
730  yield return new FileInfo(file);
731  }
732  }
733 
734  public void Delete()
735  {
736  if (!Validation.CanWrite(innerInfo.FullName, false))
737  {
738  DebugConsole.ThrowError($"Cannot delete directory \"{Name}\": modifying the contents of this folder/using this extension is not allowed.");
739  return;
740  }
741  innerInfo.Delete();
742  }
743  }
744 
745  public class FileInfo
746  {
747  private System.IO.FileInfo innerInfo;
748 
749  public FileInfo(string path)
750  {
751  innerInfo = new System.IO.FileInfo(path);
752  }
753 
754  public FileInfo(System.IO.FileInfo info)
755  {
756  innerInfo = info;
757  }
758 
759  public bool Exists => innerInfo.Exists;
760  public string Name => innerInfo.Name;
761  public string FullName => innerInfo.FullName;
762  public long Length => innerInfo.Length;
763 
764  public bool IsReadOnly
765  {
766  get
767  {
768  return innerInfo.IsReadOnly;
769  }
770  set
771  {
772  if (!Validation.CanWrite(innerInfo.FullName, false))
773  {
774  DebugConsole.ThrowError($"Cannot set read-only to {value} for \"{Name}\": modifying the files in this folder/with this extension is not allowed.");
775  return;
776  }
777  innerInfo.IsReadOnly = value;
778  }
779  }
780 
781  public void CopyTo(string dest, bool overwriteExisting = false)
782  {
783  if (!Validation.CanWrite(dest, false))
784  {
785  DebugConsole.ThrowError($"Cannot copy \"{Name}\" to \"{dest}\": modifying the contents of the destination folder is not allowed.");
786  return;
787  }
788  innerInfo.CopyTo(dest, overwriteExisting);
789  }
790 
791  public void Delete()
792  {
793  if (!Validation.CanWrite(innerInfo.FullName, false))
794  {
795  DebugConsole.ThrowError($"Cannot delete file \"{Name}\": modifying the files in this folder/with this extension is not allowed.");
796  return;
797  }
798  innerInfo.Delete();
799  }
800  }
801 }
static readonly string WorkshopModsDir
DirectoryInfo(string path)
Definition: SafeIO.cs:700
System.IO.FileAttributes Attributes
Definition: SafeIO.cs:714
IEnumerable< FileInfo > GetFiles()
Definition: SafeIO.cs:725
IEnumerable< DirectoryInfo > GetDirectories()
Definition: SafeIO.cs:716
FileInfo(string path)
Definition: SafeIO.cs:749
FileInfo(System.IO.FileInfo info)
Definition: SafeIO.cs:754
void CopyTo(string dest, bool overwriteExisting=false)
Definition: SafeIO.cs:781
override long Length
Definition: SafeIO.cs:644
override bool CanSeek
Definition: SafeIO.cs:633
FileStream(string fn, System.IO.FileStream stream)
Definition: SafeIO.cs:626
override long Seek(long offset, System.IO.SeekOrigin origin)
Definition: SafeIO.cs:675
override void Write(byte[] buffer, int offset, int count)
Definition: SafeIO.cs:663
override bool CanTimeout
Definition: SafeIO.cs:634
override bool CanRead
Definition: SafeIO.cs:632
override void SetLength(long value)
Definition: SafeIO.cs:680
override void Flush()
Definition: SafeIO.cs:685
override void Dispose(bool notCalledByFinalizer)
Definition: SafeIO.cs:690
override bool CanWrite
Definition: SafeIO.cs:636
override long Position
Definition: SafeIO.cs:647
override int Read(byte[] buffer, int offset, int count)
Definition: SafeIO.cs:658
XmlWriter(string path, System.Xml.XmlWriterSettings settings)
Definition: SafeIO.cs:152
static XmlWriter Create(string path, System.Xml.XmlWriterSettings settings)
Definition: SafeIO.cs:163
readonly System.Xml.? XmlWriter Writer
Definition: SafeIO.cs:150
void Write(System.Xml.Linq.XDocument doc)
Definition: SafeIO.cs:168