2#pragma warning disable CA1416
9using System.Diagnostics;
12using System.Runtime.InteropServices;
14using System.Runtime.InteropServices.JavaScript;
15#pragma warning disable CA1416
17using NativeFileDialogCore;
19using System.Reflection;
43 StatFs statFs =
new(
Environment.DataDirectory?.Path);
44 long num = statFs.BlockSizeLong;
45 return statFs.AvailableBlocksLong * num;
52 var paths = NSSearchPath.GetDirectories(NSSearchPathDirectory.DocumentDirectory,
53 NSSearchPathDomain.User,
true);
55 var attributes = NSFileManager.DefaultManager.GetFileSystemAttributes(paths[0]);
57 return (
long)attributes.FreeSize;
63 string fullPath = Path.GetFullPath(
ProcessPath(
"data:",
false,
false));
64 if (fullPath.Length > 0) {
66 return new DriveInfo(fullPath.Substring(0, 1)).AvailableFreeSpace;
79 string path2 =
ProcessPath(path,
false,
false, out
bool isApp);
98 throw new ArgumentException(
"openFileMode");
103 return EngineActivity.m_activity.ApplicationContext?.Assets?.Open(path2);
109 switch (openFileMode) {
110 case OpenFileMode.Create: mode = FileMode.Create;
break;
111 case OpenFileMode.CreateOrOpen: mode = FileMode.OpenOrCreate;
break;
112 default: mode = FileMode.Open;
break;
114 FileAccess access = openFileMode ==
OpenFileMode.Read ? FileAccess.Read : FileAccess.ReadWrite;
115 return File.Open(path2, mode, access, FileShare.Read);
122 public static void CopyFile(
string sourcePath,
string destinationPath) {
125 stream.CopyTo(destination);
128 public static void MoveFile(
string sourcePath,
string destinationPath) {
132 File.Move(sourceFileName, text);
153 select Path.GetFileName(s)
155 where s !=
".__override__"
158 select Path.GetFileName(s);
164 public static string ReadAllText(
string path, Encoding encoding) {
166 return streamReader.ReadToEnd();
173 public static void WriteAllText(
string path,
string text, Encoding encoding) {
175 streamWriter.Write(text);
180 return binaryReader.ReadBytes((
int)binaryReader.BaseStream.Length);
185 binaryWriter.Write(bytes);
191 int lastIndexOfPoint = path.LastIndexOf(
'.');
192 int lastIndexOfSlash = Math.Max(path.LastIndexOf(
'/'), path.LastIndexOf(
'\\'));
193 return lastIndexOfPoint >= 0 && (lastIndexOfSlash == -1 || lastIndexOfSlash < lastIndexOfPoint)
194 ? path.Substring(lastIndexOfPoint)
199 int num = Math.Max(path.LastIndexOf(
'/'), path.LastIndexOf(
'\\'));
200 return num >= 0 ? path.Substring(num + 1) : path;
205 int num = fileName.LastIndexOf(
'.');
206 return num >= 0 ? fileName.Substring(0, num) : fileName;
210 int num = path.LastIndexOf(
'/');
211 return num >= 0 ? path.Substring(0, num).TrimEnd(
'/') :
string.Empty;
215 StringBuilder stringBuilder =
new();
216 for (
int i = 0; i < paths.Length; i++) {
217 if (paths[i].Length > 0) {
218 stringBuilder.Append(paths[i]);
219 if (i < paths.Length - 1
220 && (stringBuilder.Length == 0 || stringBuilder[^1] !=
'/')) {
221 stringBuilder.Append(
'/');
225 return stringBuilder.ToString();
232 public static string ProcessPath(
string path,
bool writeAccess,
bool failIfApp) =>
ProcessPath(path, writeAccess, failIfApp, out _);
233 public static string ProcessPath(
string path,
bool writeAccess,
bool failIfApp, out
bool isApp) {
234 ArgumentNullException.ThrowIfNull(path);
235 if (Path.DirectorySeparatorChar !=
'/') {
236 path = path.Replace(
'/', Path.DirectorySeparatorChar);
238 if (Path.DirectorySeparatorChar !=
'\\') {
239 path = path.Replace(
'\\', Path.DirectorySeparatorChar);
241 if (path.StartsWith(
"app:")) {
243 throw new InvalidOperationException($
"Access denied to \"{path}\".");
246 return path.Substring(4).TrimStart(Path.DirectorySeparatorChar);
248 if (path.StartsWith(
"data:")) {
251 System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments),
252 path.Substring(5).TrimStart(Path.DirectorySeparatorChar)
255 if (path.StartsWith(
"android:")) {
258 CombinePaths(
Environment.ExternalStorageDirectory?.AbsolutePath, path.Substring(8).TrimStart(Path.DirectorySeparatorChar))
261 if (path.StartsWith(
"config:")) {
263 return Path.Combine(EngineActivity.ConfigPath, path.Substring(8).TrimStart(Path.DirectorySeparatorChar));
265 throw new InvalidOperationException($
"Invalid path \"{path}\".");
268 public static string ProcessPath(
string path,
bool writeAccess,
bool failIfApp) =>
ProcessPath(path, writeAccess, failIfApp, out _);
269 public static string ProcessPath(
string path,
bool writeAccess,
bool failIfApp, out
bool isApp) {
270 ArgumentNullException.ThrowIfNull(path);
271 if (Path.DirectorySeparatorChar !=
'/') {
272 path = path.Replace(
'/', Path.DirectorySeparatorChar);
274 if (Path.DirectorySeparatorChar !=
'\\') {
275 path = path.Replace(
'\\', Path.DirectorySeparatorChar);
277 if (path.StartsWith(
"app:") || path.StartsWith(
"data:")) {
280 System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments),
281 path.Substring(5).TrimStart(Path.DirectorySeparatorChar)
284 throw new InvalidOperationException($
"Invalid path \"{path}\".");
288 public static string GetAppDirectory(
bool failIfApp) => Path.DirectorySeparatorChar.ToString();
291 ?
throw new InvalidOperationException(
"Access denied.")
292#pragma warning disable IL3000
293 : Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location);
294#pragma warning restore IL3000
297 string text = Path.Combine(
299 Assembly.GetEntryAssembly()!.GetName()!.Name!
306 Directory.CreateDirectory(text);
314 public static string ProcessPath(
string path,
bool writeAccess,
bool failIfApp) {
315 ArgumentNullException.ThrowIfNull(path);
316 if (Path.DirectorySeparatorChar !=
'/') {
317 path = path.Replace(
'/', Path.DirectorySeparatorChar);
319 if (Path.DirectorySeparatorChar !=
'\\') {
320 path = path.Replace(
'\\', Path.DirectorySeparatorChar);
323 if (path.StartsWith(
"app:")) {
325 path = path.Substring(4).TrimStart(Path.DirectorySeparatorChar);
327 else if (path.StartsWith(
"data:")) {
329 path = path.Substring(5).TrimStart(Path.DirectorySeparatorChar);
332 if (!path.StartsWith(
"system:")) {
334 EnsurePathLinked(path);
337 throw new InvalidOperationException(
"Invalid path.");
341 path = path.Substring(7);
343 string result =
string.IsNullOrEmpty(text) ? path : Path.Combine(text, path);
345 EnsurePathLinked(result);
372 StringBuilder sanitized =
new();
373 foreach (
char c
in filename) {
376 return sanitized.ToString();
389 throw new FileNotFoundException($
"Open {path} failed, because it is not exists.");
393 Process.Start(
"explorer.exe", path);
395 Process.Start(
"xdg-open", path);
397 Window.Activity.OpenFile(path, chooserTitle, mimeType);
409 public static async Task
ShareFile(
string path,
string chooserTitle =
null,
string mimeType =
null) {
411 throw new FileNotFoundException($
"Share {path} failed, because it is not exists.");
415 Window.Activity.ShareFile(path, chooserTitle, mimeType);
419 byte[] bytes =
new byte[stream.Length];
420 _ = await stream.ReadAsync(bytes);
434#pragma warning disable CS1998
435 public static async Task<(Stream, string)>
ChooseFile(
string title =
null,
436 KeyValuePair<
string,
string[]>[] filters =
null,
437 string defaultPath =
null,
439#pragma warning restore CS1998
442 throw new ArgumentException(
"mode");
445 return await
Window.Activity.ChooseFileAsync(title);
447 throw new Exception(
"Unsupported Operation");
449 List<string> descAndExtArray = [];
450 List<int> extCounts = [];
451 if (filters !=
null) {
452 foreach (KeyValuePair<
string,
string[]> filter
in filters) {
453 descAndExtArray.Add(filter.Key);
454 extCounts.Add(filter.Value.Length);
455 string[] extensions = filter.Value;
456 foreach (
string extension
in extensions) {
457 descAndExtArray.Add(extension);
461 JSObject file =
null;
472 if (
string.IsNullOrEmpty(fileName)) {
480 return (stream, fileName);
482 string filtersString =
null;
483 if (filters !=
null) {
484 StringBuilder sb =
new();
485 bool firstAdded1 =
false;
486 foreach (KeyValuePair<
string,
string[]> filter
in filters) {
494 sb.Append(filter.Key);
496 bool firstAdded2 =
false;
497 foreach (
string ext
in filter.Value) {
508 filtersString = sb.ToString();
510 DialogResult result = Dialog.FileOpenEx(
520 && !
string.IsNullOrEmpty(result.Path)) {
522 Stream stream = File.Open(
525 mode ==
OpenFileMode.Read ? FileAccess.Read : FileAccess.ReadWrite,
530 catch (Exception e) {
531 Log.
Error($
"Choose file failed. File path: \"{result.Path}\". Reason: {e.Message}");
532 return (
null, result.Path);
540 static HashSet<string> m_linkedPaths = [
"__root__",
"dev",
"tmp"];
544 static void EnsurePathLinked(
string path) {
545 if (
string.IsNullOrEmpty(path)) {
548 path = path.Replace(
'\\',
'/');
549 string[] parts = path.Split(
'/', StringSplitOptions.RemoveEmptyEntries);
550 if (parts.Length == 0) {
553 string topDir = parts[0];
555 if (m_linkedPaths.Contains(topDir)) {
558 string linkPath = $
"/{topDir}";
559 string realPath = $
"/__root__/{topDir}";
566 m_linkedPaths.Add(topDir);
569 m_linkedPaths.Add(topDir);
572 default:
throw new Exception($
"Failed to link \"{linkPath}\" to \"{realPath}\", error code: {res}");
576 [DllImport(
"wasmfsHelper", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
577 [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
578 static extern int MountOPFS(
string mountPath);
580 [DllImport(
"wasmfsHelper", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
581 [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
582 static extern int CreateSymlink(
string target,
string linkpath);
System.Environment Environment
static partial string GetFileName(JSObject file)
static partial Task< JSObject > ShowOpenFilePicker(string[] descAndExtArray, int[] extCounts, string defaultPath)
static partial Task SaveBytesToFileHandle(JSObject fileHandle, byte[] bytes)
static partial Task< JSObject > GetFileBytes(JSObject file)
static partial Task< JSObject > ShowSaveFilePicker(string fileName, string mimeType)
static partial byte[] JSObject2ByteArray(JSObject obj)
static void Error(object message)
static void MoveFile(string sourcePath, string destinationPath)
static string GetSystemPath(string path)
static void CreateDirectory(string path)
static byte[] ReadAllBytes(string path)
static bool DirectoryExists(string path)
static void DeleteDirectory(string path, bool recursive)
static IEnumerable< string > ListDirectoryNames(string path)
static IEnumerable< string > ListFileNames(string path)
static string ChangeExtension(string path, string extension)
static Stream OpenFile(string path, OpenFileMode openFileMode)
static string GetExtension(string path)
const bool m_isAndroidPlatform
static char[] InvalidFileNameChars
static string SanitizeFileName(string filename, string replacement="-")
static FileInfo GetFileInfo(string path)
static void OpenFileWithExternalApplication(string path, string chooserTitle=null, string mimeType=null)
static DateTime GetFileLastWriteTime(string path)
static void WriteAllText(string path, string text, Encoding encoding)
static string ProcessPath(string path, bool writeAccess, bool failIfApp)
static bool FileExists(string path)
static string GetDirectoryName(string path)
static string ReadAllText(string path)
static string GetAppDirectory(bool failIfApp)
static void DeleteDirectoryRecursive(string path)
static string GetFileNameWithoutExtension(string path)
static string GetDataDirectory(bool writeAccess)
static object m_dataDirectoryCreationLock
static void MoveDirectory(string path, string newPath)
static string GetFileName(string path)
static string ReadAllText(string path, Encoding encoding)
static bool m_dataDirectoryCreated
static async Task<(Stream, string)> ChooseFile(string title=null, KeyValuePair< string, string[]>[] filters=null, string defaultPath=null, OpenFileMode mode=OpenFileMode.Read)
static long GetFileSize(string path)
static void WriteAllBytes(string path, byte[] bytes)
static void DeleteDirectory(string path)
static void DeleteFile(string path)
static string CombinePaths(params string[] paths)
static void CopyFile(string sourcePath, string destinationPath)
static DirectoryInfo GetDirectoryInfo(string path)
static void WriteAllText(string path, string text)
static async Task ShareFile(string path, string chooserTitle=null, string mimeType=null)
int MountOPFS(const char *mountPath)
int CreateSymlink(const char *target, const char *linkpath)