Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 115 additions & 6 deletions Assets/Plugins/FilePicker/FilePicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,128 @@ public static bool OpenPickFileDialog(FilterItem[] filters, Action<string> onPat
#elif UNITY_ANDROID
public static bool OpenPickFileDialog(FilterItem[] filters, Action<string> onPathReceived, string defaultPath = null)
{
if (NativeFilePicker.PickFile(
(string path) => onPathReceived(path),
NativeFilePicker.PickFile(
(string path) =>
{
if (string.IsNullOrEmpty(path))
{
Debug.LogWarning("Open file picker was cancelled or permission was denied.");
}

onPathReceived(path);
},
filters.Select(item => item.spec).ToArray()
) != NativeFilePicker.Permission.Granted)
);

return true;
}
#endif

/// <summary>
/// Opens a picker for one game set. Android uses the native multi-file
/// picker when available; desktop/editor fall back to a single selection.
/// </summary>
public static bool OpenPickFilesDialog(FilterItem[] filters, Action<string[]> onPathsReceived, string defaultPath = null)
{
#if UNITY_EDITOR
List<string> filterMapped = new();
foreach (FilterItem filter in filters)
{
filterMapped.Add(filter.name);
filterMapped.Add(filter.spec);
}

string path = UnityEditor.EditorUtility.OpenFilePanelWithFilters("Select game files", defaultPath ?? "", filterMapped.ToArray());
onPathsReceived?.Invoke(string.IsNullOrEmpty(path) ? new string[0] : new[] { path });
return true;
#elif UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX
string path = NativeFileDialog.OpenPickFileDialog(filters, defaultPath);
onPathsReceived?.Invoke(string.IsNullOrEmpty(path) ? new string[0] : new[] { path });
return true;
#elif UNITY_ANDROID
string[] allowedTypes = filters == null
? new[] { "*/*" }
: filters.Select(item => item.spec).ToArray();

if (NativeFilePicker.CanPickMultipleFiles())
{
Debug.LogError("Open file picker permission denied!");
return false;
NativeFilePicker.PickMultipleFiles(
(string[] paths) =>
{
if (paths == null || paths.Length == 0)
{
Debug.LogWarning("Open multi-file picker was cancelled or permission was denied.");
onPathsReceived?.Invoke(new string[0]);
}
else
{
onPathsReceived?.Invoke(paths);
}
},
allowedTypes
);
}
else
{
return true;
NativeFilePicker.PickFile(
(string path) => onPathsReceived?.Invoke(string.IsNullOrEmpty(path) ? new string[0] : new[] { path }),
allowedTypes
);
}

return true;
#else
onPathsReceived?.Invoke(new string[0]);
return false;
#endif
}

public static void ExportLog(string sourcePath, Action<bool> onFinished)
{
#if UNITY_EDITOR
string path = UnityEditor.EditorUtility.SaveFilePanel("Save Log", "", "onlyfun.log", "log");
if (!string.IsNullOrEmpty(path))
{
try
{
System.IO.File.Copy(sourcePath, path, true);
onFinished?.Invoke(true);
}
catch (Exception ex)
{
Debug.LogError($"Failed to export log: {ex}");
onFinished?.Invoke(false);
}
}
else
{
onFinished?.Invoke(false);
}
#elif UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX
FilterItem[] filters = new FilterItem[] { new FilterItem { name = "Log file", spec = "log" } };
string path = NativeFileDialog.OpenSaveFileDialog(filters, null, "onlyfun.log");
if (!string.IsNullOrEmpty(path))
{
try
{
System.IO.File.Copy(sourcePath, path, true);
onFinished?.Invoke(true);
}
catch (Exception ex)
{
UnityEngine.Debug.LogError($"Failed to export log: {ex}");
onFinished?.Invoke(false);
}
}
else
{
onFinished?.Invoke(false);
}
#elif UNITY_ANDROID
NativeFilePicker.ExportFile(sourcePath, success => onFinished?.Invoke(success));
#else
onFinished?.Invoke(false);
#endif
}
}
}
83 changes: 82 additions & 1 deletion Assets/Plugins/NativeFileDialog/NativeFileDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,87 @@ public static string OpenPickFileDialog(FilterItem[] filters, string defaultPath

return path;
}

[DllImport("nfd", EntryPoint = "NFD_SaveDialogU8")]
private static extern int NFD_SaveDialogU8(out IntPtr outPath, IntPtr filterList, uint count, IntPtr defaultPath, IntPtr defaultName);

public static string OpenSaveFileDialog(FilterItem[] filters, string defaultPath = null, string defaultName = null)
{
IntPtr filterList = IntPtr.Zero;
NFDU8FilterItem[] filterItems = null;
GCHandle filterListHandle = default;
IntPtr defaultPathPtr = IntPtr.Zero;
IntPtr defaultNamePtr = IntPtr.Zero;
IntPtr outPath = IntPtr.Zero;

try
{
if (filters != null)
{
filterItems = new NFDU8FilterItem[filters.Length];
for (int i = 0; i < filters.Length; i++)
{
filterItems[i].name = StringToMarshalledUtf8(filters[i].name);
filterItems[i].spec = StringToMarshalledUtf8(filters[i].spec);
}
filterListHandle = GCHandle.Alloc(filterItems, GCHandleType.Pinned);
filterList = filterListHandle.AddrOfPinnedObject();
}

if (defaultPath != null)
{
defaultPathPtr = StringToMarshalledUtf8(defaultPath);
}

if (defaultName != null)
{
defaultNamePtr = StringToMarshalledUtf8(defaultName);
}

int result = NFD_SaveDialogU8(out outPath, filterList,
filters == null ? 0 : (uint)filters.Length, defaultPathPtr, defaultNamePtr);

return result == NFD_RESULT_OK ? Marshal.PtrToStringUTF8(outPath) : null;
}
finally
{
if (outPath != IntPtr.Zero)
{
NFD_FreePathU8(outPath);
}

if (defaultPathPtr != IntPtr.Zero)
{
FreeMarshalledUtf8(defaultPathPtr);
}

if (defaultNamePtr != IntPtr.Zero)
{
FreeMarshalledUtf8(defaultNamePtr);
}

if (filterItems != null)
{
for (int i = 0; i < filterItems.Length; i++)
{
if (filterItems[i].name != IntPtr.Zero)
{
FreeMarshalledUtf8(filterItems[i].name);
}

if (filterItems[i].spec != IntPtr.Zero)
{
FreeMarshalledUtf8(filterItems[i].spec);
}
}
}

if (filterListHandle.IsAllocated)
{
filterListHandle.Free();
}
}
}
}
}
#endif
#endif
17 changes: 17 additions & 0 deletions Assets/Scripts/Data/GameDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ public bool AddGame(Model.GameInfo game)
}
}

public Model.GameInfo FindByName(string name)
{
return _connection.Table<Model.GameInfo>().FirstOrDefault(x => x.Name == name);
}

public bool UpdateGame(Model.GameInfo game)
{
try
{
return _connection.Update(game) > 0;
}
catch
{
return false;
}
}

public void RemoveGame(Model.GameInfo game)
{
_connection.Delete(game);
Expand Down
43 changes: 31 additions & 12 deletions Assets/Scripts/Module/VMGP/System/MessageBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,43 @@ public partial class VMGP
{

[ModuleCall]
private int vMsgBox(uint flags, VMString message, VMString optionalTitle)
private int vMsgBox(int flags, VMString message, VMString optionalTitle)
{
return ShowMessageBox(flags, message, optionalTitle, false);
}

[ModuleCall]
private int vMsgBoxU(int flags, VMString message, VMString optionalTitle)
{
return ShowMessageBox(flags, message, optionalTitle, true);
}

private static int ToMophunButtonValue(int uiButtonValue)
{
// Onlyfun's dialogs report the right-hand OK/Yes button as 0, while
// Mophun specifies OK/Yes as 1 and No/Cancel as 0.
return uiButtonValue == 0 ? (int)MessageBoxFlags.OK : (int)MessageBoxFlags.Cancel;
}

private int ShowMessageBox(int flags, VMString message, VMString optionalTitle, bool isUnicode)
{
uint flagBits = unchecked((uint)flags);
Severity boxSeverity;
switch (true)
{
case true when BitUtil.FlagSet(flags, MessageBoxFlags.Error):
case true when BitUtil.FlagSet(flagBits, MessageBoxFlags.Error):
boxSeverity = Severity.Error;
break;

case true when BitUtil.FlagSet(flags, MessageBoxFlags.Warning):
case true when BitUtil.FlagSet(flagBits, MessageBoxFlags.Warning):
boxSeverity = Severity.Warning;
break;

case true when BitUtil.FlagSet(flags, MessageBoxFlags.Info):
case true when BitUtil.FlagSet(flagBits, MessageBoxFlags.Info):
boxSeverity = Severity.Info;
break;

case true when BitUtil.FlagSet(flags, MessageBoxFlags.Question):
case true when BitUtil.FlagSet(flagBits, MessageBoxFlags.Question):
boxSeverity = Severity.Question;
break;

Expand All @@ -57,11 +76,11 @@ private int vMsgBox(uint flags, VMString message, VMString optionalTitle)
ButtonType buttonType;
switch (true)
{
case true when BitUtil.FlagSet(flags, MessageBoxFlags.OKCancel):
case true when BitUtil.FlagSet(flagBits, MessageBoxFlags.OKCancel):
buttonType = ButtonType.OKCancel;
break;

case true when BitUtil.FlagSet(flags, MessageBoxFlags.YesNo):
case true when BitUtil.FlagSet(flagBits, MessageBoxFlags.YesNo):
buttonType = ButtonType.YesNo;
break;

Expand All @@ -73,21 +92,21 @@ private int vMsgBox(uint flags, VMString message, VMString optionalTitle)

string title = null;

if (BitUtil.FlagSet(flags, MessageBoxFlags.Title))
if (BitUtil.FlagSet(flagBits, MessageBoxFlags.Title))
{
title = optionalTitle.Get(system.Memory);
title = optionalTitle.Get(system.Memory, isUnicode);
}

string content = message.Get(system.Memory);
string content = message.Get(system.Memory, isUnicode);
int buttonValue = 0;

// 0 is already cancel
system.UIDriver.Show(boxSeverity, title, content, buttonType, (int button) =>
{
buttonValue = button;
buttonValue = ToMophunButtonValue(button);
});

return buttonValue;
}
}
}
}
Loading