using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DynamicLua; using System.IO; using System.Drawing; using System.Windows.Forms; using Gecko; using System.Net; using System.IO.Compression; using System.ComponentModel; using System.Threading; using Newtonsoft.Json; namespace ShiftOS { public class Lua_API { public static List RunningMods = new List(); public static bool UseLuaAPI = false; } public class LuaInterpreter { public dynamic mod = new DynamicLua.DynamicLua(); public List Errors = new List(); /// /// Creates a new Lua interpreter and interprets a .lua file. /// /// The file to interpret. public LuaInterpreter(string modfile) { Errors.Clear(); //Initiate the interpreter mod = new DynamicLua.DynamicLua(); //Register core functions with the interpreter RegisterCore(); //Parse the file contents. var lua = File.ReadAllText(modfile); var t = new System.Windows.Forms.Timer(); ThisDirectory = Directory.GetParent(modfile).FullName; t.Interval = 50; t.Tick += (object se, EventArgs ea) => { if (Errors.Count > 0) { if (API.LoggerTerminal != null) { API.LoggerTerminal.WriteLine(Errors[0]); Errors.Remove(Errors[0]); } else { API.CreateInfoboxSession("Script Error", $"An error has occurred in your script: {Errors[0]}", infobox.InfoboxMode.Info); Errors.Remove(Errors[0]); } ExitScript(); } }; t.Start(); try { mod(lua); } catch (Exception ex) { API.CreateInfoboxSession("Mod Interpretation Error", "An error has occurred in your mod." + Environment.NewLine + Environment.NewLine + ex.Message, infobox.InfoboxMode.Info); } } /// /// Creates a new Lua Interpreter but doesn't interpret a file. /// public LuaInterpreter() { Errors.Clear(); //Initiate the interpreter mod = new DynamicLua.DynamicLua(); //Register core functions with the interpreter RegisterCore(); var t = new System.Windows.Forms.Timer(); t.Interval = 50; ThisDirectory = Paths.SaveRoot; t.Tick += (object se, EventArgs ea) => { if (Errors.Count > 0) { if (API.LoggerTerminal != null) { API.LoggerTerminal.WriteLine(Errors[0]); Errors.Remove(Errors[0]); } else { API.CreateInfoboxSession("Script Error", $"An error has occurred in your script: {Errors[0]}", infobox.InfoboxMode.Info); Errors.Remove(Errors[0]); } ExitScript(); } }; t.Start(); } /// /// Registers all core ShiftOS Lua functions with their C# counterparts. /// /// This is so we don't have to expose the entire source code to the interpreter. Add new functions here. /// public void RegisterCore() { //Desktop environment mod.on_unity_check += new Action((desktop, func) => { desktop.OnUnityCheck += () => { mod(func + "()"); }; }); mod.on_unity_set += new Action((desktop, func) => { desktop.OnUnityToggle += () => { mod(func + "()"); }; }); mod.on_desktop_panel_draw += new Action((desktop, func) => { desktop.OnDesktopPanelDraw += (c) => { mod(func + $"(get_panel_from_guid(\"{c}\"))"); }; }); mod.get_panel_from_guid = new Func((guid) => { foreach(var kv in API.DEF_PanelGUIDs) { if (kv.Key == guid) return kv.Value; } return null; }); mod.on_desktop_reset += new Action((desktop, func) => { desktop.OnDesktopReload += () => { mod(func + "()"); }; }); mod.on_clock_skin += new Action((desktop, func) => { desktop.OnClockSkin += () => { mod(func + "()"); }; }); mod.on_window_open += new Action((desktop, func) => { desktop.WindowOpened += (win) => { mod(func + $"(\"{API.OpenGUIDs[win]}\")"); }; }); mod.get_window = new Func((guid) => { Form frm = null; foreach(var kv in API.OpenGUIDs) { if (kv.Value == guid) frm = kv.Key; } return frm; }); mod.on_window_close += new Action((desktop, func) => { desktop.WindowClosed += (win) => { mod(func + $"(\"{API.OpenGUIDs[win]}\")"); }; }); mod.on_window_titlebar_redraw += new Action((desktop, func) => { desktop.TitlebarReset += (win) => { mod.win = win; mod(func + "(win)"); }; }); mod.on_window_border_redraw += new Action((desktop, func) => { desktop.BorderReset += (win) => { mod.win = win; mod(func + "(win)"); }; }); mod.on_window_skin += new Action((desktop, func) => { desktop.WindowSkinReset += (win) => { mod.win = win; mod(func + "(win)"); }; }); mod.get_border = new Func((Form win) => { WindowBorder b = null; foreach(Control c in win.Controls) { if (c is WindowBorder) b = c as WindowBorder; } return b; }); mod.def_update = new Action(() => API.UpdateWindows()); mod.on_app_launcher_populate += new Action((desktop, func) => { desktop.OnAppLauncherPopulate += (items) => { mod.al_items = items; mod(func + "(clr_to_table(al_items))"); }; }); mod.on_panelbutton_populate += new Action((desktop, func) => { desktop.OnPanelButtonPopulate += (items) => { mod.pb_items = items; mod(func + "(clr_to_table(pb_items))"); }; }); mod.intercept_ctrlt += new Action((func) => { API.CurrentSession.AllowCtrlTIntercept(); API.CurrentSession.CtrlTPressed += () => { mod(func + "()"); }; }); mod.stop_intercept_ctrlt += new Action(() => { API.CurrentSession.DisableCtrlTIntercept(); }); mod.on_desktopicon_populate += new Action((desktop, func) => { desktop.DesktopIconsPopulated += (items) => { mod.dl_items = items; mod(func + "(clr_to_table(dl_items))"); }; }); mod(@"function clr_to_table(clrlist) local t = {} local it = clrlist:GetEnumerator() while it:MoveNext() do t[#t+1] = it.Current end return t end"); mod.httpget = new Func((url) => { WebRequest request = WebRequest.Create(url); Stream requestStream = request.GetResponse().GetResponseStream(); StreamReader requestRead = new StreamReader(requestStream); return requestRead.ReadToEnd(); }); //Shifter Extension API mod.shifter_add_category = new Action((name) => { bool add = true; if(API.LuaShifterRegistry == null) { API.LuaShifterRegistry = new Dictionary>(); } foreach(var kv in API.LuaShifterRegistry) { if (kv.Key == name) add = false; } if(add == true) { API.LuaShifterRegistry.Add(name, new Dictionary()); } else { Errors.Add($"shifter_add_category(\"{name}\"): Error: Category already exists!"); } }); mod.shifter_remove_category = new Action((name) => { if(API.LuaShifterRegistry.ContainsKey(name)) { API.LuaShifterRegistry.Remove(name); } else { Errors.Add($"shifter_remove_category(\"{name}\"): No such category."); } }); mod.shifter_add_value = new Action((cat, name, in_value) => { if(API.LuaShifterRegistry.ContainsKey(cat)) { var lst = API.LuaShifterRegistry[cat]; if(!lst.ContainsKey(name)) { lst.Add(name, in_value); } else { Errors.Add($"shifter_add_value(\"{cat}\", \"{name}\", in_value): Category was found, but it already contained a value with the specified name."); } } else { Errors.Add($"shifter_add_value(\"{cat}\", \"{name}\", in_value): Category not found."); } }); mod.shifter_get_value = new Func((cat, name) => { if (API.LuaShifterRegistry.ContainsKey(cat)) { var lst = API.LuaShifterRegistry[cat]; if (lst.ContainsKey(name)) { return lst[name]; } else { Errors.Add($"shifter_add_value(\"{cat}\", \"{name}\", in_value): Category was found, but it already contained a value with the specified name."); return null; } } else { Errors.Add($"shifter_add_value(\"{cat}\", \"{name}\", in_value): Category not found."); return null; } }); //APIs. mod.load_api = new Action((name) => { if(File.Exists(Paths.APIs + name + ".lua")) { mod(File.ReadAllText(Paths.APIs + name + ".lua")); } }); //Functions with Return Values mod.get_app_launcher_items = new Func>(() => { var lst = new List(); API.GetAppLauncherItems(); foreach(var itm in API.AppLauncherItems) { if(itm.Display == true) { lst.Add(itm); } } return lst; }); mod.local_image = new Func((filepath) => OpenLocalImage(filepath)); mod.json_serialize = new Func((objectToSerialize) => Newtonsoft.Json.JsonConvert.SerializeObject(objectToSerialize)); mod.json_unserialize = new Func((json_string) => Newtonsoft.Json.JsonConvert.DeserializeObject(json_string)); mod.open_image = new Func((filename) => OpenImage(filename)); mod.list_add = new Action((lst, itm) => { if(lst is ListBox) { var box = lst as ListBox; box.Items.Add(itm); } }); mod.list_get_selected = new Func((lst) => { if(lst is ListBox) { return (lst as ListBox).SelectedItem?.ToString(); } else { return null; } }); mod.get_skin = new Func(() => { return API.CurrentSkin; }); mod.get_skin_images = new Func(() => { return API.CurrentSkinImages; }); mod.upgrades = new Func((id) => GetUpgrade(id)); mod.create_widget = new Func((type, text, x, y, width, height, dark_mode) => ConstructControl(type, text, x, y, width, height, dark_mode)); mod.screen_get_width = new Func(() => { return System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; }); mod.screen_get_height = new Func(() => { return System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; }); mod.create_window_borderless = new Func((x, y, width, height) => CreateForm(x, y, width, height)); mod.random = new Func((min, max) => { return new Random().Next(min, max); }); mod.color = new Func((r, g, b) => { try { return Color.FromArgb(r, g, b); } catch { Errors.Add("Invalid color values. Values must be a minimum of 0 and a maximum of 255."); return new Color(); } }); mod.get_desktop_session = new Func
(() => { return API.CurrentSession; }); mod.get_icon = new Func((id) => API.GetIcon(id)); mod.add_icon = new Action((id, img) => { if(!API.IconRegistry.ContainsKey(id)) { API.IconRegistry.Add(id, img); Skinning.Utilities.saveimages(); } }); mod.icon_exists = new Func((id) => { return API.IconRegistry.ContainsKey(id); }); mod.create_window = new Func((title, icon, width, height) => CreateForm(title, icon, width, height)); mod.get_codepoints = new Func(() => GetCP()); mod.buy_upgrade = new Func((id) => BuyUPG(id)); mod.time = new Func(() => API.GetTime()); mod.encrypt = new Func((raw) => API.Encryption.Encrypt_old(raw)); mod.decrypt = new Func((raw) => API.Encryption.Decrypt_old(raw)); mod.fread = new Func((filepath) => SafeFileRead(filepath)); mod.terminal = new Action((command) => { var t = new Terminal(); API.CreateForm(t, API.LoadedNames.TerminalName, API.GetIcon("Terminal")); t.command = command; t.DoCommand(); }); mod.fwrite = new Action((path, contents) => { if (path.StartsWith("/")) { var real_path = $"{Paths.SaveRoot}{path.Replace("/", OSInfo.DirectorySeparator)}"; if(!Directory.Exists(real_path)) { File.WriteAllText(real_path, contents); } } }); mod.add_menu_item = new Func((text, parent) => AddMenuItem(text, parent)); mod.add_child_menu_item = new Func((text, parent) => { try { var i = new ToolStripMenuItem(); i.Text = text; parent.DropDownItems.Add(i); return i; } catch(Exception ex) { Errors.Add("add_child_menu_item(): Error adding child item to parent. " + ex.Message); return null; } }); mod.set_anchor = new Action((ctrl, anchorstyle) => SetAnchor(ctrl, anchorstyle)); //Standard API Functions mod.include = new Action((filepath) => IncludeScript(filepath)); mod.log = new Action((msg) => API.Log(msg)); mod.add_codepoints = new Action((amount) => API.AddCodepoints(amount)); mod.remove_codepoints = new Action((amount) => API.RemoveCodepoints(amount)); mod.launch_mod = new Action((modSAA) => API.LaunchMod(Paths.SaveRoot + modSAA.Replace("/", OSInfo.DirectorySeparator))); mod.open_program = new Action((progname) => API.OpenProgram(progname)); mod.close_program = new Action((progname) => API.CloseProgram(progname)); mod.close_everything = new Action(() => API.CloseEverything()); mod.shutdown = new Action(() => API.ShutDownShiftOS()); mod.update_ui = new Action(() => { API.UpdateWindows(); API.CurrentSession.SetupDesktop(); }); mod.load_skin = new Action((filepath) => Skinning.Utilities.loadsknfile(filepath)); mod.save_to_skin_file = new Action((filepath) => Skinning.Utilities.saveskintofile(filepath)); mod.on_click = new Action((ctrl, funcname) => RegClick(ctrl, funcname)); mod.add_widget_to_window = new Action((win, ctrl) => AddCtrl(win, ctrl)); mod.open_file = new Action((filters, function) => OpenFile(filters, function)); mod.panel_add_widget = new Action((ctrl, parent) => { try { var p = (Panel)parent; p.Controls.Add(ctrl); } catch(Exception ex) { Errors.Add(ex.Message); } }); mod.flow_add_widget = new Action((ctrl, parent) => { try { var p = (FlowLayoutPanel)parent; p.Controls.Add(ctrl); } catch (Exception ex) { Errors.Add(ex.Message); } }); mod.info = new Action((title, message) => API.CreateInfoboxSession(title, message, infobox.InfoboxMode.Info) ); mod.on_menu_item_activate = new Action((item, function) => { item.Click += (object s, EventArgs a) => { mod($"{function}()"); }; }); mod.create_timer = new Func((interval) => { var t = new System.Windows.Forms.Timer(); t.Interval = interval; return t; }); mod.timer_on_tick = new Action((tmr, func) => { try { tmr.Tick += (object s, EventArgs a) => { mod($"{func}()"); }; } catch(Exception ex) { Errors.Add(ex.Message); } }); mod.add_widget_to_desktop = new Action((ctrl) => AddToDesktop(ctrl)); mod.set_dock = new Action((ctrl, dstyle) => { API.CurrentSession.Invoke(new Action(() => { switch (dstyle.ToLower()) { case "fill": ctrl.Dock = DockStyle.Fill; break; case "top": ctrl.Dock = DockStyle.Top; break; case "bottom": ctrl.Dock = DockStyle.Bottom; break; case "left": ctrl.Dock = DockStyle.Left; break; case "right": ctrl.Dock = DockStyle.Right; break; case "none": ctrl.Dock = DockStyle.None; break; } })); }); mod.webview_navigate = new Action((wv, url) => Navigate(wv, url)); mod.open_terminal = new Action(() => { var t = new Terminal(); API.CreateForm(t, API.LoadedNames.TerminalName, API.GetIcon("Terminal")); }); mod.create_directory = new Action((path) => { path = $"{Paths.SaveRoot}{path.Replace("/", OSInfo.DirectorySeparator)}"; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } }); mod.exists = new Func((path) => { path = $"{Paths.SaveRoot}{path.Replace("/", OSInfo.DirectorySeparator)}"; if(Directory.Exists(path)) { return true; } else if(File.Exists(path)) { return true; } else { return false; } }); mod.notify = new Action((title, message) => API.CurrentSession.AddNotification(title, message)); mod.download_file = new Action((web_address, local) => DownloadFile(web_address, local)); mod.on_key_down = new Action((ctrl, action) => RegKeyDown(ctrl, action)); mod.get_files = new Func>((path) => GetFiles(path)); mod.get_folders = new Func>((path) => GetFolders(path)); mod.zip = new Action((source, destination) => { var real = $"{Paths.SaveRoot}{source.Replace("/", OSInfo.DirectorySeparator)}"; if(Directory.Exists(real)) { var real_dest = $"{Paths.SaveRoot}{destination.Replace("/", OSInfo.DirectorySeparator)}"; ZipFile.CreateFromDirectory(real, real_dest); } else { mod.info("Script Error", "Your script tried to zip up a non-existent folder."); } }); mod.beep = new Action((freq, dur) => Beep(freq, dur)); mod.color_picker += new Action((title, oldcolor, func) => { API.CreateColorPickerSession(title, oldcolor); API.ColorPickerSession.FormClosing += (object s, FormClosingEventArgs a) => { var c = API.GetLastColorFromSession(); mod($"{func}(color({c.R}, {c.G}, {c.B}))"); }; }); mod.info_yes_no += new Action((title, message, func) => { API.CreateInfoboxSession(title, message, infobox.InfoboxMode.YesNo); API.InfoboxSession.FormClosing += (object s, FormClosingEventArgs a) => { var res = API.GetInfoboxResult(); if(res == "Yes" || res == "No") { mod($"{func}(\"{res}\")"); } }; }); //Script Management mod.exit = new Action(() => ExitScript()); mod.shutdown = new Action(() => API.ShutDownShiftOS()); mod.toggle_unity = new Action(() => API.CurrentSession.SetUnityMode()); mod.lua = new Func((luacode) => { mod(luacode); return "success"; }); mod.fileskimmer_open += new Action((filters, func) => { API.CreateFileSkimmerSession(filters, File_Skimmer.FileSkimmerMode.Open); API.FileSkimmerSession.FormClosing += (object s, FormClosingEventArgs a) => { var res = API.GetFSResult(); if(res != "fail") { var real_path = res.Replace(Paths.SaveRoot, "/").Replace("\\", "/"); mod($"{func}(\"{real_path}\")"); } }; }); mod.open_File = mod.fileskimmer_open; mod.fileskimmer_save += new Action((filters, func) => { API.CreateFileSkimmerSession(filters, File_Skimmer.FileSkimmerMode.Save); API.FileSkimmerSession.FormClosing += (object s, FormClosingEventArgs a) => { var res = API.GetFSResult(); if (res != "fail") { var real_path = res.Replace(Paths.SaveRoot, "/").Replace("\\", "/"); mod($"{func}(\"{real_path}\")"); } }; }); mod.save_File = mod.fileskimmer_save; mod.font = new Func((style, size) => { return new Font(style, size); }); //other mod.fileskimmer = new Action((folder) => OpenFS(folder)); mod.fopen = new Action((file) => OpenFile(file)); mod.loadstring = new Action((code) => { mod(code); }); //Multithreading mod.new_thread = new Func((code) => { return new Thread(() => { mod(code); }); }); mod.start_async = new Action((t) => { t.Start(); }); mod.add_applauncher_item = new Action((name, lua) => { var m = new ModApplauncherItem(); m.Name = name; m.Lua = lua; File.WriteAllText(Paths.Mod_AppLauncherEntries + m.Name, JsonConvert.SerializeObject(m)); API.UpdateWindows(); API.CurrentSession.SetupDesktop(); }); mod.get_loaded_skin = new Func(() => { return API.CurrentSkin; }); mod.reload_skin = new Action(() => { API.CurrentSession.SetupDesktop(); API.UpdateWindows(); }); mod.get_applauncher_item = new Func((name) => { ToolStripMenuItem i = null; foreach(var item in API.CurrentSession.ApplicationsToolStripMenuItem.DropDownItems) { try { ToolStripMenuItem it = (ToolStripMenuItem)item; if (it.Text == name) { i = it; } } catch { } } return i; }); mod.get_menu_item = new Func((parent, name) => { ToolStripMenuItem i = null; foreach (ToolStripMenuItem item in parent.DropDownItems) { if (item.Text == name) { i = item; } } return i; }); GC.Collect(); } /// /// Sends a keydown event to Lua when you press a key on the specified control. /// /// Control to assign the event to. /// Function to call on keydown. public void RegKeyDown(Control ctrl, string action) { /* */ ctrl.KeyDown += (object s, KeyEventArgs a) => { mod($"{action}(\"{a.KeyCode.ToString().ToLower()}\")"); }; } /// /// Gets a list of files. /// /// Directory to scan. /// A System.Collections.Generic.List of all files. public List GetFiles(string dir) { if (Directory.Exists($"{Paths.SaveRoot}{dir.Replace("/", OSInfo.DirectorySeparator)}")) { var luatable = new List(); foreach (string val in Directory.GetFiles($"{Paths.SaveRoot}{dir.Replace("/", OSInfo.DirectorySeparator)}")) { luatable.Add(val); } return luatable; } else { return null; } } /// /// Gets a list of folders. /// /// Directory to scan. /// A System.Collections.Generic.List of all folders. public List GetFolders(string dir) { if (Directory.Exists($"{Paths.SaveRoot}{dir.Replace("/", OSInfo.DirectorySeparator)}")) { var luatable = new List(); foreach(string val in Directory.GetDirectories($"{Paths.SaveRoot}{dir.Replace("/", OSInfo.DirectorySeparator)}")) { luatable.Add(val); } return luatable; } else { return null; } } public string ThisDirectory = null; /// /// Downloads a file. /// /// Web URL to download /// A ShiftOS path to download to. public void DownloadFile(string web, string local) { var wc = new WebClient(); try { var real_path = $"{Paths.SaveRoot}{local.Replace("/", OSInfo.DirectorySeparator)}"; wc.DownloadFile(web, real_path); mod.notify("Download complete", "Successfully downloaded file " + web + " from the Internet."); } catch(Exception ex) { mod.print("Could not download remote file " + web + ", " + ex.Message); } } /// /// Interprets a script within this interpreter. /// /// Script file. public void IncludeScript(string filename) { var real_file = $"{ThisDirectory}{filename.Replace("/", OSInfo.DirectorySeparator)}"; var lua = File.ReadAllText(real_file); try { mod(lua); } catch(Exception ex) { mod.info("Script Error", "An error has occurred in your script: " + ex.Message); } } /// /// Open a file skimmer in the specified directory. /// /// Directory to open in. public void OpenFS(string dir) { var f = new File_Skimmer(); API.CreateForm(f, API.LoadedNames.FileSkimmerName, Properties.Resources.iconFileSkimmer); if(dir.StartsWith("/")) { var real = dir; var real_slash = real.Replace("/", OSInfo.DirectorySeparator); var real_path = $"{Paths.SaveRoot}{real_slash}"; f.CurrentFolder = real_path; f.ListFiles(); } } /// /// Opens a file in the right program. /// /// The file path. Why this is named "dir", which means DIRECTORY, not FILE, by the way, is beyond me. public void OpenFile(string dir) { var f = new File_Skimmer(); if (dir.StartsWith("/")) { var real = dir; var real_slash = real.Replace("/", OSInfo.DirectorySeparator); var real_path = $"{Paths.SaveRoot}{real_slash}"; f.OpenFile(real_path); } } public List OpenForms = new List(); /// /// Exits the script. What did you think it would do? /// public void ExitScript() { foreach(Form f in OpenForms) { f.Close(); } } /// /// Set control anchor from Lua. /// /// Target control /// Anchor string (for example "top;left;bottom;right" or "top;left" or "top") public void SetAnchor(Control ctrl, string anchor) { var a = AnchorStyles.None; var l = anchor.ToLower(); if(l.Contains("left")) { a = a | AnchorStyles.Left; } if (l.Contains("right")) { a = a | AnchorStyles.Right; } if (l.Contains("bottom")) { a = a | AnchorStyles.Bottom; } if (l.Contains("top")) { a = a | AnchorStyles.Bottom; } ctrl.Anchor = a; } /// /// Navigate a webview to the specified URL. /// /// The webview control. YES, We use Gecko, not Internet Exploder. /// The target URL, for example "http://playshiftos.ml/forum" public void Navigate(GeckoWebBrowser wv, string url) { wv.Navigate(url); } /// /// Add a control to the desktop. /// /// The control to add. public void AddToDesktop(Control ctrl) { API.CurrentSession.Controls.Add(ctrl); } /// /// Add a child to a menu item. /// /// New item's text /// New item's parent. /// The new item. public ToolStripMenuItem AddMenuItem(string text, MenuStrip parent) { var itm = new ToolStripMenuItem(); itm.Text = text; itm.Tag = "menu_item"; parent.Items.Add(itm); return itm; } /// /// Allows the user to get a user to open a file. /// /// File filter. /// Function to call on select. public void OpenFile(string fi, string fu) { API.CreateFileSkimmerSession(fi, File_Skimmer.FileSkimmerMode.Open); API.FileSkimmerSession.FormClosing += (object s, FormClosingEventArgs a) => { mod($"{fu}(\"{API.GetFSResult().Replace(Paths.SaveRoot, "").Replace("\\", "/")}\")"); }; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Prompt user to save a file. /// /// File filters. /// Function to call. public void SaveFile(string fi, string fu) { API.CreateFileSkimmerSession(fi, File_Skimmer.FileSkimmerMode.Save); API.FileSkimmerSession.FormClosing += (object s, FormClosingEventArgs a) => { mod($"{fu}(\"{API.GetFSResult().Replace(Paths.SaveRoot, "").Replace("\\", "/")}\")"); }; } /// /// Safely read a file. /// /// File to read. /// Contents of the file. public string SafeFileRead(string path) { string contents = ""; if(path.StartsWith("/")) { var real = $"{Paths.SaveRoot}{path.Replace("\\", "/")}"; if(File.Exists(real)) { contents = File.ReadAllText(real); } else { Errors.Add("fread(): File not found."); } } else { Errors.Add("fread(): Path not valid."); } return contents; } /// /// Buy a shiftorium upgrade. /// /// Upgrade ID. /// Did the upgrade get bought successfully? public bool BuyUPG(string id) { if(API.Upgrades.ContainsKey(id)) { bool bought = false; foreach(Shiftorium.Upgrade upg in SaveSystem.ShiftoriumRegistry.DefaultUpgrades) { if(upg.id == id) { bought = Shiftorium.Utilities.Buy(upg); } } return bought; } else { //Upgrade doesn't exist. return false; } } /// /// Checks if an upgrade is bought. /// /// Upgrade ID. /// Whether or not it is bought public bool GetUpgrade(string id) { if(API.Upgrades.ContainsKey(id)) { return API.Upgrades[id]; } else { //Upgrade doesn't exist. return false; } } /// /// Gets the current amount of Codepoints. /// /// Can you read? Sorry, it's just... I don't feel like typing the same thing twice... public int GetCP() { return API.CurrentSave.codepoints; } /// /// Constructs a WinForms control. /// /// Control type. /// Control text. /// X coordinate. /// Y coordinate. /// Width. /// Height. /// Is it dark? /// The control, all ShiftOS-ified for you. public Control ConstructControl(string type, string text, int x, int y, int width, int height, bool darkmode) { var ctrl = new Control(); switch(type.ToLower()) { case "luatextbox": var stxt = new SyntaxRichTextBox(); stxt.Text = text; stxt.SetLanguage(SyntaxSettings.Language.Lua); ctrl = stxt; break; case "list": var lst = new ListBox(); ctrl = lst; break; case "button": var btn = new Button(); btn.FlatStyle = FlatStyle.Flat; if(darkmode) { //Set dark button btn.ForeColor = API.CurrentSkin.titletextcolour; btn.BackColor = API.CurrentSkin.titlebarcolour; } else { btn.BackColor = Color.White; btn.ForeColor = Color.Black; } ctrl = (Control)btn; break; case "webview": var g = new Gecko.GeckoWebBrowser(); g.NoDefaultContextMenu = true; ctrl = (Gecko.GeckoWebBrowser)g; //This control renders HTML, so therefore a dark theme is futile. break; case "menustrip": ctrl = new MenuStrip(); ctrl.Tag = "menustrip"; //Menu Strips are rendered using a custom renderer, thus, DarkMode is not required. break; case "panel": ctrl = new Panel(); if(darkmode) { ctrl.BackColor = API.CurrentSkin.titlebarcolour; ctrl.ForeColor = API.CurrentSkin.titletextcolour; } else { ctrl.BackColor = Color.White; ctrl.ForeColor = Color.Black; } break; case "flow": ctrl = new FlowLayoutPanel(); if(darkmode) { ctrl.BackColor = API.CurrentSkin.titlebarcolour; ctrl.ForeColor = API.CurrentSkin.titletextcolour; } else { ctrl.BackColor = Color.White; ctrl.ForeColor = Color.Black; } break; case "label": ctrl = new Label(); //Text Color and Back Color inherited from parent. break; case "textbox": ctrl = new TextBox(); if(darkmode) { ctrl.BackColor = API.CurrentSkin.titlebarcolour; ctrl.ForeColor = API.CurrentSkin.titletextcolour; } else { ctrl.BackColor = Color.White; ctrl.ForeColor = Color.Black; } break; case "richtextbox": ctrl = new RichTextBox(); if(darkmode) { ctrl.BackColor = API.CurrentSkin.titlebarcolour; ctrl.ForeColor = API.CurrentSkin.titletextcolour; } else { ctrl.BackColor = Color.White; ctrl.ForeColor = Color.Black; } break; default: ctrl = new Control(); if(darkmode) { ctrl.BackColor = API.CurrentSkin.titlebarcolour; ctrl.ForeColor = API.CurrentSkin.titletextcolour; } else { ctrl.BackColor = Color.White; ctrl.ForeColor = Color.Black; } break; } ctrl.Text = text; ctrl.Name = text.ToLower().Replace(" ", "_"); ctrl.Location = new Point(x, y); ctrl.Size = new Size(width, height); ctrl.Visible = true; return ctrl; } /// /// Broken, piece of dump beep function. /// /// Frequency. /// Length. public void Beep(int freq, int duration) { Beeper.Play(freq, duration); } /// /// Adds a control to a window. /// /// Target window /// Control to add. public void AddCtrl(Form win, Control ctrl) { List borders = new List(); foreach(Control c in win.Controls) { if(c.Name == "api_brdr") { var b = (WindowBorder)c; b.pgcontents.Controls.Add(ctrl); ctrl.BringToFront(); borders.Add(b); } } if(borders.Count == 0) { win.Controls.Add(ctrl); } } /// /// Fire a click event when you click the control. /// /// Target control /// Function to call. public void RegClick(Control ctrl, string funcname) { ctrl.MouseDown += (object s, MouseEventArgs a) => { if (a.Button == MouseButtons.Left) { mod($"{funcname}()"); } }; } /// /// Creates a ShiftOS window. /// /// Window title. /// Window icon /// Width /// Height /// The new window public Form CreateForm(string title, Image img, int width, int height) { GC.Collect(); //Create new Form instance. var f = new Form(); //Set size of form if(width < 100) { width = 100; } if(height < 100) { height = 100; } f.Size = new Size(width, height); //ShiftOSify it. API.CreateForm(f, title, img); //Add to list of forms that should be closed on script exit OpenForms.Add(f); //Return it. return f; } /// /// Creates a borderless window. /// /// Starting X coordinate. /// Starting Y coordinate /// Width /// Height /// The new window. public Form CreateForm(int x, int y, int width, int height) { GC.Collect(); //Create new Form instance. var f = new Form(); //Set size of form if (width < 100) { width = 100; } if (height < 100) { height = 100; } f.Size = new Size(width, height); f.FormBorderStyle = FormBorderStyle.None; f.Location = new Point(x, y); f.Show(); //Add to list of forms that should be closed on script exit OpenForms.Add(f); //Return it. return f; } /// /// Opens an image file. /// /// File path /// Loaded image public Image OpenLocalImage(string filepath) { if (filepath.StartsWith("/")) { var real = $"{ThisDirectory}{filepath.Replace("/", OSInfo.DirectorySeparator)}"; if (File.Exists(real)) { try { return Image.FromFile(real); } catch (Exception ex) { Errors.Add(ex.Message); return null; } } else { Errors.Add($"open_image({filepath}): File not found."); return null; } } else { Errors.Add($"open_image({filepath}): Not a valid file path."); return null; } } /// /// Opens an image file. /// /// File path /// Loaded image public Image OpenImage(string filepath) { if (filepath.StartsWith("/")) { var real = $"{Paths.SaveRoot}{filepath.Replace("/", OSInfo.DirectorySeparator)}"; if(File.Exists(real)) { try { return Image.FromFile(real); } catch (Exception ex) { Errors.Add(ex.Message); return null; } } else { Errors.Add($"open_image({filepath}): File not found."); return null; } } else { Errors.Add($"open_image({filepath}): Not a valid file path."); return null; } } } public class Beeper { static Thread _beepThread; static AutoResetEvent _signalBeep; static bool _beeping = false; static Beeper() { _signalBeep = new AutoResetEvent(false); _beepThread = new Thread(() => { for (;;) { while(_beeping == true) { } _beeping = true; Thread.Sleep(_freq); Console.Beep(_freq, _dur); _beeping = false; } }, 1); _beepThread.IsBackground = true; _beepThread.Start(); } static int _freq = 38; static int _dur = 1000; public static void Play(int freq, int dur) { _freq = freq; if (_freq <= 37) { _freq = 38; } _dur = dur; _signalBeep.Set(); } } }