aboutsummaryrefslogtreecommitdiff
path: root/ShiftOS_TheReturn
diff options
context:
space:
mode:
Diffstat (limited to 'ShiftOS_TheReturn')
-rw-r--r--ShiftOS_TheReturn/AppearanceManager.cs263
-rw-r--r--ShiftOS_TheReturn/AudioManager.cs143
-rw-r--r--ShiftOS_TheReturn/Commands.cs887
-rw-r--r--ShiftOS_TheReturn/CrashHandler.cs225
-rw-r--r--ShiftOS_TheReturn/Desktop.cs286
-rw-r--r--ShiftOS_TheReturn/KernelWatchdog.cs151
-rw-r--r--ShiftOS_TheReturn/Localization.cs240
-rw-r--r--ShiftOS_TheReturn/NotificationDaemon.cs141
-rw-r--r--ShiftOS_TheReturn/OutOfBoxExperience.cs11
-rw-r--r--ShiftOS_TheReturn/Paths.cs276
-rw-r--r--ShiftOS_TheReturn/SaveSystem.cs565
-rw-r--r--ShiftOS_TheReturn/Scripting.cs716
-rw-r--r--ShiftOS_TheReturn/ServerManager.cs313
-rw-r--r--ShiftOS_TheReturn/ShiftOS.Engine.csproj2
-rw-r--r--ShiftOS_TheReturn/ShiftnetSite.cs53
-rw-r--r--ShiftOS_TheReturn/Shiftorium.cs563
-rw-r--r--ShiftOS_TheReturn/Skinning.cs1570
-rw-r--r--ShiftOS_TheReturn/Story.cs119
-rw-r--r--ShiftOS_TheReturn/TerminalBackend.cs623
-rw-r--r--ShiftOS_TheReturn/TerminalTextWriter.cs160
-rw-r--r--ShiftOS_TheReturn/TutorialManager.cs8
-rw-r--r--ShiftOS_TheReturn/UniteClient.cs236
-rw-r--r--ShiftOS_TheReturn/UniteTestCommands.cs74
-rw-r--r--ShiftOS_TheReturn/VirusEngine.cs106
24 files changed, 7544 insertions, 187 deletions
diff --git a/ShiftOS_TheReturn/AppearanceManager.cs b/ShiftOS_TheReturn/AppearanceManager.cs
new file mode 100644
index 0000000..81858d3
--- /dev/null
+++ b/ShiftOS_TheReturn/AppearanceManager.cs
@@ -0,0 +1,263 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define MUD_RAPIDDEV
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using Newtonsoft.Json;
+using static ShiftOS.Engine.SaveSystem;
+
+namespace ShiftOS.Engine
+{
+ // Provides functionality for managing windows within ShiftOS.
+ public static class AppearanceManager
+ {
+ [Obsolete("Please use Localization.GetAllLanguages().")]
+ public static string[] GetLanguages()
+ {
+ return Localization.GetAllLanguages();
+ }
+
+ // Sets the title text of the specified window.
+ public static void SetWindowTitle(IShiftOSWindow window, string title)
+ {
+ if (window == null)
+ throw new ArgumentNullException("window", "The window cannot be null.");
+ winmgr.SetTitle(window, title);
+ }
+
+ //HEY LETS FIND THE WINDOWS
+ public static IEnumerable<Type> GetAllWindowTypes()
+ {
+ List<Type> types = new List<Type>();
+ foreach(var file in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ // hey if a thing is an exe or a dll show up plz kthx
+ if(file.EndsWith(".exe") || file.EndsWith(".dll"))
+ {
+ try
+ {
+ var asm = Assembly.LoadFile(file);
+ foreach(var type in asm.GetTypes())
+ {
+ if (type.GetInterfaces().Contains(typeof(IShiftOSWindow)))
+ types.Add(type);
+ }
+ }
+ catch { }
+ }
+ }
+ return types;
+ }
+
+ // hey you know that window we just made appear? well give it its title
+ public static string GetDefaultTitle(Type winType)
+ {
+ if (winType == null)
+ throw new ArgumentNullException("winType");
+ foreach(var attrib in winType.GetCustomAttributes(false))
+ {
+ if(attrib is DefaultTitleAttribute)
+ {
+ return (attrib as DefaultTitleAttribute).Title;
+ }
+ }
+ return winType.Name;
+ }
+
+ // Current cursor position of the console
+ public static int CurrentPosition { get; set; }
+
+ // We don't know what this does. It may be gone if it does nothing.
+ public static int LastLength { get; set; }
+
+
+ // Minimize a window.
+ public static void Minimize(IWindowBorder form)
+ {
+ if (form == null)
+ //FUCK WHY THE FUCK IS THIS NULL
+ throw new ArgumentNullException("form");
+ if (winmgr == null)
+ //FUCK THIS PART OF THE ENGINE WASNT TURNED ON YET
+ throw new EngineModuleDisabledException();
+ winmgr.Minimize(form);
+ }
+
+ // Maximizes a window! :D
+ public static void Maximize(IWindowBorder form)
+ {
+ if (form == null)
+ //AHHHH SHOULDNT BE NULLLLLL
+ throw new ArgumentNullException("form");
+ if (winmgr == null)
+ //WHY ARE YOU DOING THIS PART OF THE ENGINE IT WASNT ENABLED FUCK
+ throw new EngineModuleDisabledException();
+ winmgr.Maximize(form);
+ }
+
+
+ // Provides a list of all open ShiftOS windows.
+ public static List<IWindowBorder> OpenForms = new List<IWindowBorder>();
+
+ // Decorates a window with a border, then shows the window.
+ public static void SetupWindow(IShiftOSWindow form)
+ {
+ if (form == null)
+ //YOU GET THE POINT THIS REALLY SHOULDNT BE NULL
+ throw new ArgumentNullException("form");
+ if (winmgr == null)
+ //SAME HERE
+ throw new EngineModuleDisabledException();
+ winmgr.SetupWindow(form);
+ Desktop.ResetPanelButtons();
+ }
+
+ // Closes the specified window. SHOCKED YOU ARE I KNOW, HOW COULD YOU HAVE GUESSED
+ public static void Close(IShiftOSWindow win)
+ {
+ if (win == null)
+ //NOPE SHOULDNT BE NULL
+ throw new ArgumentNullException("win");
+ if (winmgr == null)
+ //WHY IS THIS NULL
+ throw new EngineModuleDisabledException();
+ winmgr.Close(win);
+ Desktop.ResetPanelButtons();
+ }
+
+
+ // Decorates a window with a border, then shows the window, as a dialog box.
+ public static void SetupDialog(IShiftOSWindow form)
+ {
+ if (form == null)
+ //NULLLLLLLLL
+ throw new ArgumentNullException("form");
+ if (winmgr == null)
+ //ASGDFHASDGF
+ throw new EngineModuleDisabledException();
+ winmgr.SetupDialog(form);
+ Desktop.ResetPanelButtons();
+ }
+
+ // The underlying window manager for this engine module
+ private static WindowManager winmgr = null;
+
+ // Initiate this engine module, and perform mandatory configuration.
+ public static void Initiate(WindowManager mgr)
+ {
+ winmgr = mgr; // A working, configured window manager to use as a backend for this module
+ }
+
+
+ // Raised when the engine is entering its shutdown phase. Save your work!
+ public static event EmptyEventHandler OnExit;
+
+ // Starts the engine's exit routine, firing the OnExit event.
+ internal static void Exit()
+ {
+ OnExit?.Invoke();
+ //disconnect from MUD
+ ServerManager.Disconnect();
+ Desktop.InvokeOnWorkerThread(() =>
+ {
+ Environment.Exit(0); //bye bye
+ });
+ }
+
+ // The current terminal body control.
+ public static ITerminalWidget ConsoleOut { get; set; }
+
+ // Redirects the .NET to a new TerminalTextWriter instance.
+ public static void StartConsoleOut()
+ {
+ Console.SetOut(new TerminalTextWriter()); //"plz start writing text .NET kthx"
+ }
+
+ // Invokes an action on the window management thread.
+ public static void Invoke(Action act)
+ {
+ winmgr.InvokeAction(act);
+ }
+ }
+
+ // Provides the base functionality for a ShiftOS terminal.
+ public interface ITerminalWidget
+ {
+ void Write(string text); // Actually write text to this Terminal! :D:D:D:D
+ void WriteLine(string text); // Write text to this Terminal, followed by a newline.
+ void Clear(); // Clear the contents of this Terminal, i bet you wouldve never guessed that
+ void SelectBottom(); // Move the cursor to the last character in the Terminal.
+ }
+
+ // makes the window manager actually do its job
+ public abstract class WindowManager
+ {
+ public abstract void Minimize(IWindowBorder border); // guess what this does
+ public abstract void Maximize(IWindowBorder border); // ooh this too
+ public abstract void Close(IShiftOSWindow win); // omg this probably does something
+ public abstract void SetupWindow(IShiftOSWindow win); // i cant think of what this does
+ public abstract void SetupDialog(IShiftOSWindow win); // how about this???????
+ public abstract void InvokeAction(Action act); // i wonder what this invokes
+ public abstract void SetTitle(IShiftOSWindow win, string title); // what is a title again
+ }
+
+ // Provides the base functionality for a typical ShiftOS window border, what did you expect
+ public interface IWindowBorder
+ {
+ void Close(); // CLOSES THE BORDER ALONG WITH ITS WINDOW!!!!!!! HOLY SHIT I DIDNT EXPECT THAT
+ string Text { get; set; } // title text exists now
+ IShiftOSWindow ParentWindow { get; set; } // Gets or sets the underlying for this border.
+ }
+
+ // Provides a way of setting default title text for classes.
+ public class DefaultTitleAttribute : Attribute
+ {
+ // oy if you cant find a title this is the one you should use
+ public DefaultTitleAttribute(string title)
+ {
+ Title = title;
+ }
+
+ public string Title { get; private set; }
+ }
+
+ // An exception that is thrown when mandatory configuration to run a specific method or module hasn't been done yet.
+ public class EngineModuleDisabledException : Exception
+ {
+ // FUCK WE DIDNT ORDER THINGS RIGHT
+ public EngineModuleDisabledException() : base("This engine module has not yet been enabled.")
+ {
+ //FUCK
+ }
+ }
+}
diff --git a/ShiftOS_TheReturn/AudioManager.cs b/ShiftOS_TheReturn/AudioManager.cs
new file mode 100644
index 0000000..553a1d9
--- /dev/null
+++ b/ShiftOS_TheReturn/AudioManager.cs
@@ -0,0 +1,143 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define NOSOUND
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using NAudio;
+using NAudio.Wave;
+
+namespace ShiftOS.Engine
+{
+ public static class AudioManager
+ {
+ private static WaveOut _out = null;
+ private static AudioFileReader _reader = null;
+ private static IAudioProvider _provider = null;
+
+ /// <summary>
+ /// Stops the current sound if one is playing and disposes of the sound.
+ /// </summary>
+ public static void Stop()
+ {
+ Desktop.InvokeOnWorkerThread(() =>
+ {
+ _out?.Stop();
+ _reader?.Dispose();
+ _out?.Dispose();
+ });
+ }
+
+ /// <summary>
+ /// Initiates this engine module using an <see cref="IAudioProvider"/> as a backend for selecting background soundtrack as well as the volume level for the sound.
+ /// </summary>
+ /// <param name="_p">A background soundtrack and volume provider.</param>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="_p"/> is null.</exception>
+ public static void Init(IAudioProvider _p)
+ {
+ if (_p == null)
+ throw new ArgumentNullException("_p");
+ _provider = _p;
+ }
+
+ /// <summary>
+ /// Sets the volume of the audio system.
+ /// </summary>
+ /// <param name="volume">The volume to use, from 0 to 1.</param>
+ public static void SetVolume(float volume)
+ {
+ _provider.Volume = volume; //persist between songs
+ _out.Volume = volume;
+ }
+
+ /// <summary>
+ /// Plays a specified sound file.
+ /// </summary>
+ /// <param name="file">The file to play.</param>
+ public static void Play(string file)
+ {
+ try
+ {
+ _reader = new AudioFileReader(file);
+ _out = new WaveOut();
+ _out.Init(_reader);
+ _out.Play();
+ if (SaveSystem.CurrentSave == null)
+ _out.Volume = 1.0f;
+ else
+ _out.Volume = (float)SaveSystem.CurrentSave.MusicVolume / 100;
+ _out.PlaybackStopped += (o, a) => { PlayCompleted?.Invoke(); };
+ }
+ catch(Exception ex)
+ {
+ Console.WriteLine("Audio error: " + ex.Message);
+ }
+ }
+
+ /// <summary>
+ /// Writes the data in the specified <see cref="Stream"/> to a file, and plays it as a sound file.
+ /// </summary>
+ /// <param name="str">The stream to read from.</param>
+ public static void PlayStream(Stream str)
+ {
+ var bytes = new byte[str.Length];
+ str.Read(bytes, 0, bytes.Length);
+ ShiftOS.Engine.AudioManager.Stop();
+ if (File.Exists("snd.wav"))
+ File.Delete("snd.wav");
+ File.WriteAllBytes("snd.wav", bytes);
+
+ ShiftOS.Engine.AudioManager.Play("snd.wav");
+
+ }
+
+ public static event Action PlayCompleted;
+ }
+
+ public interface IAudioProvider
+ {
+ /// <summary>
+ /// Gets a byte[] array corresponding to an MP3 track given an index.
+ /// </summary>
+ /// <param name="index">A track index to use when finding the right track.</param>
+ /// <returns>The MP3 byte[] array.</returns>
+ byte[] GetTrack(int index);
+
+ /// <summary>
+ /// Gets the 1-based count of all available tracks.
+ /// </summary>
+ int Count { get; }
+
+ /// <summary>
+ /// Gets or sets the track player's volume.
+ /// </summary>
+ float Volume { get; set; }
+ }
+}
diff --git a/ShiftOS_TheReturn/Commands.cs b/ShiftOS_TheReturn/Commands.cs
new file mode 100644
index 0000000..6e59311
--- /dev/null
+++ b/ShiftOS_TheReturn/Commands.cs
@@ -0,0 +1,887 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define DEVEL
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using ShiftOS.Engine.Properties;
+using System.IO;
+using Newtonsoft.Json;
+using System.IO.Compression;
+
+using ShiftOS.Objects;
+using Discoursistency.Base.Models.Authentication;
+using ShiftOS.Engine.Scripting;
+using ShiftOS.Objects.ShiftFS;
+
+namespace ShiftOS.Engine
+{
+ [Namespace("infobox", hide = true)]
+ [RequiresUpgrade("desktop;wm_free_placement")]
+ public static class InfoboxDebugCommands
+ {
+
+ [RequiresArgument("title")]
+ [RequiresArgument("msg")]
+ [Command("show")]
+ public static bool ShowInfo(Dictionary<string, object> args)
+ {
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ Infobox.Show(args["title"].ToString(), args["msg"].ToString());
+ }));
+ return true;
+ }
+
+ [RequiresArgument("title")]
+ [RequiresArgument("msg")]
+ [Command("yesno")]
+ public static bool ShowYesNo(Dictionary<string, object> args)
+ {
+ bool forwarding = TerminalBackend.IsForwardingConsoleWrites;
+ var fGuid = TerminalBackend.ForwardGUID;
+ Action<bool> callback = (result) =>
+ {
+ TerminalBackend.IsForwardingConsoleWrites = forwarding;
+ TerminalBackend.ForwardGUID = (forwarding == true) ? fGuid : null;
+ string resultFriendly = (result == true) ? "yes" : "no";
+ Console.WriteLine($"{SaveSystem.CurrentUser.Username} says {resultFriendly}.");
+ TerminalBackend.IsForwardingConsoleWrites = false;
+ };
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ Infobox.PromptYesNo(args["title"].ToString(), args["msg"].ToString(), callback);
+
+ }));
+ return true;
+ }
+
+ [RequiresArgument("title")]
+ [RequiresArgument("msg")]
+ [Command("text")]
+ public static bool ShowText(Dictionary<string, object> args)
+ {
+ bool forwarding = TerminalBackend.IsForwardingConsoleWrites;
+ var fGuid = TerminalBackend.ForwardGUID;
+ Action<string> callback = (result) =>
+ {
+ TerminalBackend.IsForwardingConsoleWrites = forwarding;
+ TerminalBackend.ForwardGUID = (forwarding == true) ? fGuid : null;
+ Console.WriteLine($"{SaveSystem.CurrentSave.Username} says \"{result}\".");
+ TerminalBackend.IsForwardingConsoleWrites = false;
+ };
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ Infobox.PromptText(args["title"].ToString(), args["msg"].ToString(), callback);
+ }));
+ return true;
+ }
+
+ }
+
+ [Namespace("audio")]
+ public static class AudioCommands
+ {
+ [Command("setvol", description = "Set the volume of the system audio to anywhere between 0 and 100.")]
+ [RequiresArgument("value")]
+ [RequiresUpgrade("audio_volume")]
+ public static bool SetVolume(Dictionary<string,object> args)
+ {
+ int val = Convert.ToInt32(args["value"].ToString());
+ float volume = (val / 100F);
+ AudioManager.SetVolume(volume);
+ return true;
+ }
+ [RequiresUpgrade("audio_volume")]
+ [Command("mute", description = "Sets the volume of the system audio to 0")]
+ public static bool MuteAudio()
+ {
+ AudioManager.SetVolume(0);
+ return true;
+ }
+ }
+
+ [RequiresUpgrade("mud_fundamentals")]
+ [Namespace("mud")]
+ public static class MUDCommands
+ {
+ [MultiplayerOnly]
+ [Command("status")]
+ public static bool Status()
+ {
+ ServerManager.PrintDiagnostics();
+ return true;
+ }
+
+ [Command("connect")]
+ public static bool Connect(Dictionary<string, object> args)
+ {
+ try
+ {
+ string ip = (args.ContainsKey("addr") == true) ? args["addr"] as string : "michaeltheshifter.me";
+ int port = (args.ContainsKey("port") == true) ? Convert.ToInt32(args["port"] as string) : 13370;
+ try
+ {
+ ServerManager.Initiate(ip, port);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("{ERROR}: " + ex.Message);
+ }
+
+ TerminalBackend.PrefixEnabled = false;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error running script:" + ex);
+ return false;
+ }
+ }
+
+ [Command("reconnect")]
+ [RequiresUpgrade("hacker101_deadaccts")]
+ public static bool Reconnect()
+ {
+ Console.WriteLine("--reconnecting to multi-user domain...");
+ KernelWatchdog.MudConnected = true;
+ Console.WriteLine("--done.");
+ return true;
+ }
+
+ [MultiplayerOnly]
+ [Command("disconnect")]
+ [RequiresUpgrade("hacker101_deadaccts")]
+ public static bool Disconnect()
+ {
+ Console.WriteLine("--connection to multi-user domain severed...");
+ KernelWatchdog.MudConnected = false;
+ return true;
+ }
+
+ [MultiplayerOnly]
+ [Command("sendmsg")]
+ [KernelMode]
+ [RequiresUpgrade("hacker101_deadaccts")]
+ [RequiresArgument("header")]
+ [RequiresArgument("body")]
+ public static bool SendMessage(Dictionary<string, object> args)
+ {
+ ServerManager.SendMessage(args["header"].ToString(), args["body"].ToString());
+ return true;
+ }
+ }
+
+ [TutorialLock]
+ [Namespace("trm")]
+ public static class TerminalCommands
+ {
+ [Command("clear")]
+ public static bool Clear()
+ {
+ AppearanceManager.ConsoleOut.Clear();
+ return true;
+ }
+
+ [Command("echo")]
+ [RequiresArgument("text")]
+ public static bool Echo(Dictionary<string, object> args)
+ {
+ Console.WriteLine(args["text"]);
+ return true;
+ }
+ }
+
+#if DEVEL
+ internal class Rock : Exception
+ {
+ internal Rock() : base("Someone threw a rock at the window, and the Terminal shattered.")
+ {
+
+ }
+ }
+
+ [MultiplayerOnly]
+ [Namespace("dev")]
+ public static class ShiftOSDevCommands
+ {
+ [Command("buy")]
+ public static bool UnlockUpgrade(Dictionary<string, object> args)
+ {
+ string upg = args["id"].ToString();
+ try
+ {
+ SaveSystem.CurrentSave.Upgrades[upg] = true;
+ Shiftorium.InvokeUpgradeInstalled();
+ SaveSystem.SaveGame();
+ }
+ catch
+ {
+ Console.WriteLine("Upgrade not found.");
+ }
+ return true;
+ }
+
+ [Command("rock", description = "A little surprise for unstable builds...")]
+ public static bool ThrowASandwichingRock()
+ {
+ Infobox.Show("He who lives in a glass house shouldn't throw stones...", new Rock().Message);
+ return false;
+ }
+
+
+ [Command("unbuy")]
+ [RequiresArgument("upgrade")]
+ public static bool UnbuyUpgrade(Dictionary<string, object> args)
+ {
+ try
+ {
+ SaveSystem.CurrentSave.Upgrades[args["upgrade"].ToString()] = false;
+ SaveSystem.SaveGame();
+ Desktop.PopulateAppLauncher();
+ Desktop.CurrentDesktop.SetupDesktop();
+ }
+ catch
+ {
+ Console.WriteLine("Upgrade not found.");
+ }
+ return true;
+ }
+
+ [Command("getallupgrades")]
+ public static bool GetAllUpgrades()
+ {
+ Console.WriteLine(JsonConvert.SerializeObject(SaveSystem.CurrentSave.Upgrades, Formatting.Indented));
+ return true;
+ }
+
+ [Command("multarg")]
+ [RequiresArgument("id")]
+ [RequiresArgument("name")]
+ [RequiresArgument("type")]
+ public static bool MultArg(Dictionary<string, object> args)
+ {
+ Console.WriteLine("Success! "+args.ToString());
+ return true;
+ }
+
+ [Command("restart")]
+ public static bool Restart()
+ {
+ SaveSystem.CurrentSave.Upgrades = new Dictionary<string, bool>();
+ SaveSystem.CurrentSave.Codepoints = 0;
+ SaveSystem.CurrentSave.StoriesExperienced.Clear();
+ SaveSystem.CurrentSave.StoriesExperienced.Add("mud_fundamentals");
+ SaveSystem.SaveGame();
+ Shiftorium.InvokeUpgradeInstalled();
+ return true;
+ }
+
+ [Command("freecp")]
+ public static bool FreeCodepoints(Dictionary<string, object> args)
+ {
+ if (args.ContainsKey("amount"))
+ try
+ {
+ long codepointsToAdd = Convert.ToInt64(args["amount"].ToString());
+ SaveSystem.CurrentSave.Codepoints += codepointsToAdd;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("{ERROR}: " + ex.Message);
+ return true;
+ }
+
+ SaveSystem.CurrentSave.Codepoints += 1000;
+ return true;
+ }
+
+ [Command("unlockeverything")]
+ public static bool UnlockAllUpgrades()
+ {
+ foreach (var upg in Shiftorium.GetDefaults())
+ {
+ Shiftorium.Buy(upg.ID, 0);
+ }
+ return true;
+ }
+
+ [Command("info")]
+ public static bool DevInformation()
+ {
+ Console.WriteLine("{SHIFTOS_PLUS_MOTTO}");
+ Console.WriteLine("{SHIFTOS_VERSION_INFO}" + Assembly.GetExecutingAssembly().GetName().Version);
+ return true;
+ }
+ [Command("pullfile")]
+ public static bool PullFile(Dictionary<string, object> args)
+ {
+ if (args.ContainsKey("physical") && args.ContainsKey("virtual"))
+ {
+ string file = (string)args["physical"];
+ string dest = (string)args["virtual"];
+ if (System.IO.File.Exists(file))
+ {
+ Console.WriteLine("Pulling physical file to virtual drive...");
+ byte[] filebytes = System.IO.File.ReadAllBytes(file);
+ ShiftOS.Objects.ShiftFS.Utils.WriteAllBytes(dest, filebytes);
+ }
+ else
+ {
+ Console.WriteLine("The specified file does not exist on the physical drive.");
+ }
+ }
+ else
+ {
+ Console.WriteLine("You must supply a physical path.");
+ }
+ return true;
+ }
+ [Command("crash")]
+ public static bool CrashInstantly()
+ {
+ CrashHandler.Start(new Exception("ShiftOS was sent a command to forcefully crash."));
+ return true;
+ }
+ }
+#endif
+
+ [Namespace("sos")]
+ public static class ShiftOSCommands
+ {
+ [Command("setsfxvolume", description = "Set the volume of various sound effects to a value between 1 and 100.")]
+ [RequiresArgument("value")]
+ public static bool SetSfxVolume(Dictionary<string, object> args)
+ {
+ int value = int.Parse(args["value"].ToString());
+ if (value >= 0 && value <= 100)
+ {
+ SaveSystem.CurrentSave.SfxVolume = value;
+ SaveSystem.SaveGame();
+ }
+ else
+ {
+ Console.WriteLine("Volume must be between 0 and 100!");
+ }
+ return true;
+ }
+
+ [Command("setmusicvolume", description ="Set the music volume to a value between 1 and 100.")]
+ [RequiresArgument("value")]
+ public static bool SetMusicVolume(Dictionary<string, object> args)
+ {
+ int value = int.Parse(args["value"].ToString());
+ if(value >= 0 && value <= 100)
+ {
+ SaveSystem.CurrentSave.MusicVolume = value;
+ SaveSystem.SaveGame();
+ }
+ else
+ {
+ Console.WriteLine("Volume must be between 0 and 100!");
+ }
+ return true;
+ }
+
+ [RemoteLock]
+ [Command("shutdown")]
+ public static bool Shutdown()
+ {
+ TerminalBackend.InvokeCommand("sos.save");
+ AppearanceManager.Exit();
+ return true;
+ }
+
+ [Command("lang", "{COMMAND_SOS_LANG_USAGE}", "{COMMAND_SOS_LANG_DESCRIPTION}")]
+ [RequiresArgument("language")]
+ public static bool SetLanguage(Dictionary<string, object> userArgs)
+ {
+ try
+ {
+ string lang = "";
+
+ if (userArgs.ContainsKey("language"))
+ lang = (string)userArgs["language"];
+ else
+ throw new Exception("You must specify a valid 'language' value.");
+
+ if (Localization.GetAllLanguages().Contains(lang))
+ {
+ SaveSystem.CurrentSave.Language = lang;
+ SaveSystem.SaveGame();
+ Console.WriteLine("{LANGUAGE_CHANGED}");
+ return true;
+ }
+
+ throw new Exception($"Couldn't find language with ID: {lang}");
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ [Command("help", "{COMMAND_HELP_USAGE}", "{COMMAND_HELP_DESCRIPTION}")]
+ public static bool Help()
+ {
+ foreach (var exec in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ if (exec.EndsWith(".exe") || exec.EndsWith(".dll"))
+ {
+ try
+ {
+ var asm = Assembly.LoadFile(exec);
+
+ var types = asm.GetTypes();
+
+ foreach (var type in types)
+ {
+ if (Shiftorium.UpgradeAttributesUnlocked(type))
+ {
+ foreach (var a in type.GetCustomAttributes(false))
+ {
+ if (a is Namespace)
+ {
+ var ns = a as Namespace;
+
+ if (!ns.hide)
+ {
+ string descp = "{NAMESPACE_" + ns.name.ToUpper() + "_DESCRIPTION}";
+ if (descp == Localization.Parse(descp))
+ descp = "";
+ else
+ descp = Shiftorium.UpgradeInstalled("help_description") ? Localization.Parse("{SEPERATOR}" + descp) : "";
+
+ Console.WriteLine($"{{NAMESPACE}}{ns.name}" + descp);
+
+ foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
+ {
+ if (Shiftorium.UpgradeAttributesUnlocked(method))
+ {
+ foreach (var ma in method.GetCustomAttributes(false))
+ {
+ if (ma is Command)
+ {
+ var cmd = ma as Command;
+
+ if (!cmd.hide)
+ {
+ string descriptionparse = "{COMMAND_" + ns.name.ToUpper() + "_" + cmd.name.ToUpper() + "_DESCRIPTION}";
+ string usageparse = "{COMMAND_" + ns.name.ToUpper() + "_" + cmd.name.ToUpper() + "_USAGE}";
+ if (descriptionparse == Localization.Parse(descriptionparse))
+ descriptionparse = "";
+ else
+ descriptionparse = Shiftorium.UpgradeInstalled("help_description") ? Localization.Parse("{SEPERATOR}" + descriptionparse) : "";
+
+ if (usageparse == Localization.Parse(usageparse))
+ usageparse = "";
+ else
+ usageparse = Shiftorium.UpgradeInstalled("help_usage") ? Localization.Parse("{SEPERATOR}" + usageparse, new Dictionary<string, string>() {
+ {"%ns", ns.name},
+ {"%cmd", cmd.name}
+ }) : "";
+
+ Console.WriteLine($"{{COMMAND}}{ns.name}.{cmd.name}" + usageparse + descriptionparse);
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ }
+ catch { }
+ }
+ }
+
+ return true;
+ }
+
+ [MultiplayerOnly]
+ [Command("save")]
+ public static bool Save()
+ {
+ SaveSystem.SaveGame();
+ return true;
+ }
+
+ [MultiplayerOnly]
+ [Command("status")]
+ public static bool Status()
+ {
+ string status = $@"ShiftOS version {Assembly.GetExecutingAssembly().GetName().Version.ToString()}
+
+Codepoints: {SaveSystem.CurrentSave.Codepoints}
+Upgrades: {SaveSystem.CurrentSave.CountUpgrades()} installed,
+ {Shiftorium.GetAvailable().Length} available";
+
+ if (Shiftorium.UpgradeInstalled("mud_control_centre"))
+ status += Environment.NewLine + $"Reputation: {SaveSystem.CurrentSave.RawReputation} ({SaveSystem.CurrentSave.Reputation})";
+ Console.WriteLine(status);
+ return true;
+ }
+ }
+
+ [MultiplayerOnly]
+ [Namespace("shiftorium")]
+ public static class ShiftoriumCommands
+ {
+ [Command("buy")]
+ [RequiresArgument("upgrade")]
+ public static bool BuyUpgrade(Dictionary<string, object> userArgs)
+ {
+ try
+ {
+ string upgrade = "";
+
+ if (userArgs.ContainsKey("upgrade"))
+ upgrade = (string)userArgs["upgrade"];
+ else
+ throw new Exception("You must specify a valid 'upgrade' value.");
+
+ foreach (var upg in Shiftorium.GetAvailable())
+ {
+ if (upg.ID == upgrade)
+ {
+ Shiftorium.Buy(upgrade, upg.Cost);
+ return true;
+ }
+ }
+
+ throw new Exception($"Couldn't find upgrade with ID: {upgrade}");
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ [RequiresUpgrade("shiftorium_bulk_buy")]
+ [Command("bulkbuy")]
+ [RequiresArgument("upgrades")]
+ public static bool BuyBulk(Dictionary<string, object> args)
+ {
+ if (args.ContainsKey("upgrades"))
+ {
+ string[] upgrade_list = (args["upgrades"] as string).Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var upg in upgrade_list)
+ {
+ var dict = new Dictionary<string, object>();
+ dict.Add("upgrade", upg);
+ BuyUpgrade(dict);
+ }
+ }
+ else
+ {
+ throw new Exception("Please specify a list of upgrades in the 'upgrades' argument. Each upgrade is separated by a comma.");
+ }
+ return true;
+ }
+
+
+ [Command("info")]
+ public static bool ViewInfo(Dictionary<string, object> userArgs)
+ {
+ try
+ {
+ string upgrade = "";
+
+ if (userArgs.ContainsKey("upgrade"))
+ upgrade = (string)userArgs["upgrade"];
+ else
+ throw new Exception("You must specify a valid 'upgrade' value.");
+
+ foreach (var upg in Shiftorium.GetDefaults())
+ {
+ if (upg.ID == upgrade)
+ {
+ Console.WriteLine($@"Information for {upgrade}:
+
+{upg.Category}: {upg.Name} - {upg.Cost} Codepoints
+------------------------------------------------------
+
+{upg.Description}
+
+To buy this upgrade, run:
+shiftorium.buy{{upgrade:""{upg.ID}""}}");
+ return true;
+ }
+ }
+
+ throw new Exception($"Couldn't find upgrade with ID: {upgrade}");
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ [Command("categories")]
+ public static bool ListCategories()
+ {
+ foreach(var cat in Shiftorium.GetCategories())
+ {
+ Console.WriteLine($"{cat} - {Shiftorium.GetAvailable().Where(x=>x.Category==cat).Count()} upgrades");
+ }
+ return true;
+ }
+
+ [Command("list")]
+ public static bool ListAll(Dictionary<string, object> args)
+ {
+ try
+ {
+ bool showOnlyInCategory = false;
+
+ string cat = "Other";
+
+ if (args.ContainsKey("cat"))
+ {
+ showOnlyInCategory = true;
+ cat = args["cat"].ToString();
+ }
+
+ Dictionary<string, long> upgrades = new Dictionary<string, long>();
+ int maxLength = 5;
+
+ IEnumerable<ShiftoriumUpgrade> upglist = Shiftorium.GetAvailable();
+ if (showOnlyInCategory)
+ {
+ if (Shiftorium.IsCategoryEmptied(cat))
+ {
+ ConsoleEx.Bold = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("Shiftorium Query Error");
+ Console.WriteLine();
+ ConsoleEx.Bold = false;
+ ConsoleEx.ForegroundColor = ConsoleColor.Gray;
+ Console.WriteLine("Either there are no upgrades in the category \"" + cat + "\" or the category was not found.");
+ return true;
+ }
+ upglist = Shiftorium.GetAvailable().Where(x => x.Category == cat);
+ }
+
+
+ if(upglist.Count() == 0)
+ {
+ ConsoleEx.Bold = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("No upgrades available!");
+ Console.WriteLine();
+ ConsoleEx.Bold = false;
+ ConsoleEx.ForegroundColor = ConsoleColor.Gray;
+ Console.WriteLine("You have installed all available upgrades for your system. Please check back later for more.");
+ return true;
+
+ }
+ foreach (var upg in upglist)
+ {
+ if (upg.ID.Length > maxLength)
+ {
+ maxLength = upg.ID.Length;
+ }
+
+ upgrades.Add(upg.ID, upg.Cost);
+ }
+
+ Console.WriteLine("ID".PadRight((maxLength + 5) - 2) + "Cost (Codepoints)");
+
+ foreach (var upg in upgrades)
+ {
+ Console.WriteLine(upg.Key.PadRight((maxLength + 5) - upg.Key.Length) + " " + upg.Value.ToString());
+ }
+ return true;
+ }
+ catch (Exception e)
+ {
+ CrashHandler.Start(e);
+ return false;
+ }
+ }
+ }
+
+ [Namespace("win")]
+ public static class WindowCommands
+ {
+
+
+
+ [RemoteLock]
+ [Command("list")]
+ public static bool List()
+ {
+ Console.WriteLine("Window ID\tName");
+ foreach (var app in AppearanceManager.OpenForms)
+ {
+ //Windows are displayed the order in which they were opened.
+ Console.WriteLine($"{AppearanceManager.OpenForms.IndexOf(app)}\t{app.Text}");
+ }
+ return true;
+ }
+
+ [RemoteLock]
+ [Command("open")]
+ public static bool Open(Dictionary<string, object> args)
+ {
+ try
+ {
+ if (args.ContainsKey("app"))
+ {
+ var app = args["app"] as string;
+ //ANNND now we start reflecting...
+ foreach (var asmExec in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ if (asmExec.EndsWith(".exe") || asmExec.EndsWith(".dll"))
+ {
+ var asm = Assembly.LoadFile(asmExec);
+ try
+ {
+ foreach (var type in asm.GetTypes())
+ {
+ if (type.BaseType == typeof(UserControl))
+ {
+ foreach (var attr in type.GetCustomAttributes(false))
+ {
+ if (attr is WinOpenAttribute)
+ {
+ if (app == (attr as WinOpenAttribute).ID)
+ {
+ if (SaveSystem.CurrentSave.Upgrades.ContainsKey(app))
+ {
+ if (Shiftorium.UpgradeInstalled(app))
+ {
+ IShiftOSWindow frm = Activator.CreateInstance(type) as IShiftOSWindow;
+ AppearanceManager.SetupWindow(frm);
+ return true;
+ }
+ else
+ {
+ throw new Exception($"{app} was not found on your system! Try looking in the shiftorium...");
+ }
+ }
+ else
+ {
+ IShiftOSWindow frm = Activator.CreateInstance(type) as IShiftOSWindow;
+ AppearanceManager.SetupWindow(frm);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch { }
+
+ }
+ }
+
+ }
+ else
+ {
+ foreach (var asmExec in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ if (asmExec.EndsWith(".exe") || asmExec.EndsWith(".dll"))
+ {
+ try
+ {
+ var asm = Assembly.LoadFile(asmExec);
+
+ foreach (var type in asm.GetTypes())
+ {
+ if (type.GetInterfaces().Contains(typeof(IShiftOSWindow)))
+ {
+ foreach (var attr in type.GetCustomAttributes(false))
+ {
+ if (attr is WinOpenAttribute)
+ {
+ if (Shiftorium.UpgradeAttributesUnlocked(type))
+ {
+ Console.WriteLine("win.open{app:\"" + (attr as WinOpenAttribute).ID + "\"}");
+ }
+ }
+ }
+ }
+ }
+ }
+ catch { }
+ }
+ }
+
+
+ return true;
+ }
+ Console.WriteLine("Couldn't find the specified app on your system.");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error running script:" + ex);
+ return false;
+ }
+ }
+
+ [RemoteLock]
+ [Command("close", usage = "{win:integer32}", description ="Closes the specified window.")]
+ [RequiresArgument("win")]
+ [RequiresUpgrade("close_command")]
+ public static bool CloseWindow(Dictionary<string, object> args)
+ {
+ int winNum = -1;
+ if (args.ContainsKey("win"))
+ winNum = Convert.ToInt32(args["win"].ToString());
+ string err = null;
+
+ if (winNum < 0 || winNum >= AppearanceManager.OpenForms.Count)
+ err = "The window number must be between 0 and " + (AppearanceManager.OpenForms.Count - 1).ToString() + ".";
+
+ if (string.IsNullOrEmpty(err))
+ {
+ Console.WriteLine($"Closing {AppearanceManager.OpenForms[winNum].Text}...");
+ AppearanceManager.Close(AppearanceManager.OpenForms[winNum].ParentWindow);
+ }
+ else
+ {
+ Console.WriteLine(err);
+ }
+
+ return true;
+ }
+
+ }
+}
diff --git a/ShiftOS_TheReturn/CrashHandler.cs b/ShiftOS_TheReturn/CrashHandler.cs
new file mode 100644
index 0000000..48eaf1f
--- /dev/null
+++ b/ShiftOS_TheReturn/CrashHandler.cs
@@ -0,0 +1,225 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reflection;
+using System.Management;
+using System.Windows.Forms;
+using Newtonsoft.Json;
+using System.Threading;
+using System.IO;
+
+namespace ShiftOS.Engine
+{
+ public class GetHardwareInfo
+ {
+ // returns the processor's name for the crash
+ public static string GetProcessorName()
+ {
+ string ProcessorName = ""; // put the processors name in here sometime later
+ ManagementObjectSearcher mos
+ = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor"); // OI CPU TELL ME YOUR NAME PLZ
+
+ foreach (ManagementObject mo in mos.Get())
+ ProcessorName = mo["Name"].ToString(); // see told you it puts in it there
+
+ return ProcessorName;
+ }
+ // same as above but instead for the gpu's name for the crash as well
+ public static string GetGPUName()
+ {
+ string GPUName = "";
+ ManagementObjectSearcher mos
+ = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_VideoController"); //now can you tell me your name cpu kthx
+
+ foreach (ManagementObject mo in mos.Get())
+ GPUName = mo["Name"].ToString();
+
+ return GPUName;
+ }
+ // oh wow even more same, but this time its RAM AMOUNT oooh nice
+ public static string GetRAMAmount()
+ {
+ var RAMAmount = "";
+ ManagementObjectSearcher mos
+ = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PhysicalMemory"); //ram you too how much of you exists
+
+ foreach (ManagementObject mo in mos.Get())
+ RAMAmount = mo["Capacity"].ToString();
+
+ RAMAmount = (RAMAmount + " B"); // ooh and now we add "bytes" to the end
+
+ return RAMAmount;
+ }
+ }
+
+
+ public partial class CrashHandler : Form
+ {
+ //fuck it crashed
+ public CrashHandler()
+ {
+ InitializeComponent();
+
+
+ //Send the bug to Unite as a bug report
+ // or alternatively, send to [email protected]
+ // or just on the discord that works too
+
+ }
+
+ public static Exception HandledException = null; // this value determines if we can try to set the game back on track or we cant do anything about it
+
+ public static void Start(Exception e)
+ {
+ if(SaveSystem.CurrentSave != null)
+ TerminalBackend.InvokeCommand("sos.save"); // SAVE BEFORE CRASHING
+ ServerManager.Disconnect();
+
+ while (Application.OpenForms.Count > 0)
+ Application.OpenForms[0].Close();
+
+ //Set our global exception variable, and show the exception dialog.
+ HandledException = e;
+ System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
+ System.IO.FileInfo fileInfo = new System.IO.FileInfo(assembly.Location);
+ DateTime lastModified = fileInfo.LastWriteTime;
+
+ // put all this in a text document
+ string rtbcrash_Text = $@" === {AssemblyName} has crashed. ===
+
+Game: {AssemblyName}
+Description: {AssemblyDescription}
+
+Basic Information For User:
+---------------------------------
+
+When: {DateTime.Now.ToString()}
+Why: {HandledException.Message}
+What: {HandledException.GetType().Name}
+
+We, at the ShiftOS Development Team, apologise for your game crash,
+we will take this bug report seriously - and it has been emailed
+to the development team of ShiftOS, thank you for enjoying our game!
+
+Advanced Information (for experts and developers):
+----------------------------------------------------
+
+Host system information:
+---------------------------------
+
+Operating system: {Environment.OSVersion.Platform.ToString()}
+Version: {Environment.OSVersion.VersionString}
+Is 64-bit: {Environment.Is64BitOperatingSystem}
+ShiftOS exec path: {Application.ExecutablePath}
+
+Advanced Host Information:
+---------------------------------
+
+CPU Name: {GetHardwareInfo.GetProcessorName()}
+Physical RAM Installed: {GetHardwareInfo.GetRAMAmount()}
+GPU Name: {GetHardwareInfo.GetGPUName()}
+
+ShiftOS basic information:
+---------------------------------
+
+ShiftOS Version: {Assembly.GetExecutingAssembly().GetName().Version}
+ShiftOS Date: {lastModified.ToString()}
+
+ShiftOS environment information:
+---------------------------------
+
+Is Save loaded: {(SaveSystem.CurrentSave != null)}
+Paths loaded in system: {JsonConvert.SerializeObject(Paths.GetAll())}
+
+
+Crash: {HandledException.GetType().Name}
+--------------------------------------------
+
+Exception message: {HandledException.Message}
+HResult (this is technical): {HandledException.HResult}
+Has inner exception: {(HandledException.InnerException != null)}
+Stack trace:
+{HandledException.StackTrace}";
+
+ if (HandledException.InnerException != null)
+ {
+ var i = HandledException.InnerException;
+ rtbcrash_Text += $@"
+
+Inner: {i.GetType().Name}
+--------------------------------------------
+
+Exception message: {i.Message}
+HResult (this is technical): {i.HResult}
+Stack trace:
+{i.StackTrace}";
+
+ }
+
+ File.WriteAllText("crash.txt", rtbcrash_Text); // make that text document and put above super long string in it
+ var result = MessageBox.Show(caption: "ShiftOS - Fatal error", text: "ShiftOS has encountered a fatal error and has been shut down. Info about the error has been saved to a file called crash.txt in the same folder as the active executable. Would you like to try and recover the game session?", buttons: MessageBoxButtons.YesNo);
+ if(result == DialogResult.Yes)
+ {
+ Application.Restart(); // tries to restart if user clicks yes, who wouldve guessed
+ }
+ }
+
+ private void button1_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+
+ private void btnjump_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ Application.Restart();
+ }
+
+ // make both of those variables that appear in the long string above
+ public static string AssemblyName { get; private set; }
+ public static string AssemblyDescription { get; private set; }
+
+ // get info about the game itself
+ public static void SetGameMetadata(Assembly assembly)
+ {
+ AssemblyName = assembly.GetName().Name; // name of game
+ foreach(var attr in assembly.GetCustomAttributes(true))
+ {
+ if(attr is AssemblyDescriptionAttribute)
+ {
+ AssemblyDescription = (attr as AssemblyDescriptionAttribute).Description; // description of the game
+ }
+ }
+
+ }
+ }
+}
diff --git a/ShiftOS_TheReturn/Desktop.cs b/ShiftOS_TheReturn/Desktop.cs
new file mode 100644
index 0000000..a5e7f43
--- /dev/null
+++ b/ShiftOS_TheReturn/Desktop.cs
@@ -0,0 +1,286 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using ShiftOS.Objects.ShiftFS;
+using static ShiftOS.Engine.SkinEngine;
+
+namespace ShiftOS.Engine
+{
+ /// <summary>
+ /// Denotes that this class is launchable from the App Launcher.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+ public class LauncherAttribute : Attribute
+ {
+ /// <summary>
+ /// Marks this form as a launcher item that, when clicked, will open the form.
+ /// </summary>
+ /// <param name="name">The text displayed on the launcher item</param>
+ /// <param name="requiresUpgrade">Whether or not an upgrade must be installed to see the launcher</param>
+ /// <param name="upgradeID">The ID of the upgrade - leave blank if requiresUpgrade is false.</param>
+ /// <param name="category">The category that the item will appear in.</param>
+ public LauncherAttribute(string name, bool requiresUpgrade, string upgradeID = "", string category = "Other")
+ {
+ Category = category;
+ Name = name;
+ RequiresUpgrade = requiresUpgrade;
+ ID = upgradeID;
+ }
+
+ /// <summary>
+ /// Gets or sets the name of the launcher item
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets whether this entry requires a Shiftorium upgrade.
+ /// </summary>
+ public bool RequiresUpgrade { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ID of the required upgrade.
+ /// </summary>
+ public string ID { get; set; }
+
+ /// <summary>
+ /// Gets or sets this item's category.
+ /// </summary>
+ public string Category { get; private set; }
+
+ /// <summary>
+ /// Gets whether or not the required upgrade is installed.
+ /// </summary>
+ public bool UpgradeInstalled
+ {
+ get
+ {
+ if (!RequiresUpgrade)
+ return true;
+
+ return Shiftorium.UpgradeInstalled(ID);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Provides core functionality for a typical ShiftOS desktop.
+ /// </summary>
+ public interface IDesktop
+ {
+ /// <summary>
+ /// Gets the name of the desktop.
+ /// </summary>
+ string DesktopName { get; }
+
+ /// <summary>
+ /// Show a notification on the desktop.
+ /// </summary>
+ /// <param name="app">An application ID (for determining what system icon to show the notification alongside)</param>
+ /// <param name="title">The title of the notification.</param>
+ /// <param name="message">Isn't this.... self explanatory?</param>
+ void PushNotification(string app, string title, string message);
+
+ /// <summary>
+ /// Performs most of the skinning and layout handling for the desktop.
+ /// </summary>
+ void SetupDesktop();
+
+ /// <summary>
+ /// Hides the currently-opened app launcher menu.
+ /// </summary>
+ void HideAppLauncher();
+
+
+ /// <summary>
+ /// Populates the app launcher menu.
+ /// </summary>
+ /// <param name="items">All items to be placed in the menu.</param>
+ void PopulateAppLauncher(LauncherItem[] items);
+
+ /// <summary>
+ /// Handles desktop-specific routines for showing ShiftOS windows.
+ /// </summary>
+ /// <param name="border">The calling window.</param>
+ void ShowWindow(IWindowBorder border);
+
+ /// <summary>
+ /// Handles desktop-specific routines for closing ShiftOS windows.
+ /// </summary>
+ /// <param name="border">The calling window.</param>
+ void KillWindow(IWindowBorder border);
+
+ /// <summary>
+ /// Populates the panel button list with all open windows.
+ /// </summary>
+ void PopulatePanelButtons();
+
+ /// <summary>
+ /// Performs desktop-specific routines for minimizing a window.
+ /// </summary>
+ /// <param name="brdr">The calling window.</param>
+ void MinimizeWindow(IWindowBorder brdr);
+
+
+ /// <summary>
+ /// Performs desktop-specific routines for maximizing a window.
+ /// </summary>
+ /// <param name="brdr">The calling window.</param>
+ void MaximizeWindow(IWindowBorder brdr);
+
+
+ /// <summary>
+ /// Performs desktop-specific routines for restoring a window to its default state.
+ /// </summary>
+ /// <param name="brdr">The calling window.</param>
+ void RestoreWindow(IWindowBorder brdr);
+
+ /// <summary>
+ /// Invokes an action on the UI thread.
+ /// </summary>
+ /// <param name="act">The action to invoke.</param>
+ void InvokeOnWorkerThread(Action act);
+
+ /// <summary>
+ /// Calculates the screen size of the desktop.
+ /// </summary>
+ /// <returns>The desktop's screen size.</returns>
+ Size GetSize();
+
+ /// <summary>
+ /// Opens the app launcher at a specific point.
+ /// </summary>
+ /// <param name="loc">Where the app launcher should be opened.</param>
+ void OpenAppLauncher(Point loc);
+
+ /// <summary>
+ /// Opens the desktop.
+ /// </summary>
+ void Show();
+
+ /// <summary>
+ /// Closes the desktop.
+ /// </summary>
+ void Close();
+ }
+
+ public static class Desktop
+ {
+ /// <summary>
+ /// The underlying desktop object.
+ /// </summary>
+ private static IDesktop _desktop = null;
+
+ public static Size Size
+ {
+ get
+ {
+ return _desktop.GetSize();
+ }
+ }
+
+ public static IDesktop CurrentDesktop
+ {
+ get
+ {
+ return _desktop;
+ }
+ }
+
+ public static void Init(IDesktop desk, bool show = false)
+ {
+ IDesktop deskToClose = null;
+ if (_desktop != null)
+ deskToClose = _desktop;
+ _desktop = desk;
+ if (show == true)
+ _desktop.Show();
+ deskToClose?.Close();
+ }
+
+ public static void MinimizeWindow(IWindowBorder brdr)
+ {
+ _desktop.MinimizeWindow(brdr);
+ }
+
+ public static void MaximizeWindow(IWindowBorder brdr)
+ {
+ _desktop.MaximizeWindow(brdr);
+ }
+
+ public static void RestoreWindow(IWindowBorder brdr)
+ {
+ _desktop.RestoreWindow(brdr);
+ }
+
+
+ public static void InvokeOnWorkerThread(Action act)
+ {
+ _desktop.InvokeOnWorkerThread(act);
+ }
+
+ public static void ResetPanelButtons()
+ {
+ _desktop.PopulatePanelButtons();
+ }
+
+ public static void ShowWindow(IWindowBorder brdr)
+ {
+ _desktop.ShowWindow(brdr);
+ }
+
+ public static void PopulateAppLauncher()
+ {
+ _desktop.PopulateAppLauncher(AppLauncherDaemon.Available().ToArray());
+ }
+
+ public static void OpenAppLauncher(Point loc)
+ {
+ _desktop.OpenAppLauncher(loc);
+ }
+
+ public static void HideAppLauncher()
+ {
+ _desktop.HideAppLauncher();
+ }
+
+ public static void PushNotification(string app, string title, string msg)
+ {
+ InvokeOnWorkerThread(() =>
+ {
+ _desktop.PushNotification(app, title, msg);
+ });
+ }
+ }
+ // sorry i almost killed everything :P
+}
diff --git a/ShiftOS_TheReturn/KernelWatchdog.cs b/ShiftOS_TheReturn/KernelWatchdog.cs
new file mode 100644
index 0000000..66ec1f7
--- /dev/null
+++ b/ShiftOS_TheReturn/KernelWatchdog.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using static ShiftOS.Objects.ShiftFS.Utils;
+
+namespace ShiftOS.Engine
+{
+ public static class KernelWatchdog
+ {
+ //store logs into a file
+ public static void Log(string e, string desc)
+ {
+ string line = $"[{DateTime.Now}] <{e}> {desc}";
+ if (FileExists("0:/system/data/kernel.log"))
+ {
+ string contents = ReadAllText("0:/system/data/kernel.log");
+ contents += Environment.NewLine + line;
+ WriteAllText("0:/system/data/kernel.log", contents);
+ }
+ else
+ {
+ WriteAllText("0:/system/data/kernel.log", line);
+ }
+ }
+
+ private static bool _mudConnected = true;
+
+ public static bool InKernelMode { get; private set; }
+ public static bool MudConnected
+ {
+ get
+ {
+ return _mudConnected;
+ }
+ set
+ {
+ if(value == false) // hey game if you want to disconnect from mud do this:
+ {
+ foreach(var win in AppearanceManager.OpenForms)
+ {
+ foreach(var attr in win.ParentWindow.GetType().GetCustomAttributes(true))
+ {
+ // prevents disconnect from mud if an application that needs a connection is open
+ if(attr is MultiplayerOnlyAttribute)
+ {
+ ConsoleEx.Bold = true;
+ ConsoleEx.Underline = false;
+ ConsoleEx.Italic = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.Red;
+ Console.Write("Error:");
+ ConsoleEx.Bold = false;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine("Cannot disconnect from multi-user domain because an app that depends on it is open.");
+ TerminalBackend.PrintPrompt();
+ return;
+ }
+ }
+ }
+ }
+
+ _mudConnected = value; // connects or disconnects from mud
+ Desktop.PopulateAppLauncher();
+ }
+ }
+
+ //determines if user is root
+ public static bool IsSafe(Type type)
+ {
+ if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root)
+ return true;
+
+ foreach (var attrib in type.GetCustomAttributes(false))
+ {
+ if (attrib is KernelModeAttribute)
+ {
+ if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root)
+ return true;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ //also determines if user is root, only for a method instead
+ public static bool IsSafe(MethodInfo type)
+ {
+ if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root)
+ return true;
+
+ foreach (var attrib in type.GetCustomAttributes(false))
+ {
+ if (attrib is KernelModeAttribute)
+ {
+ if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root)
+ return true;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static string regularUsername = ""; //put regular username in here later
+
+
+ public static void EnterKernelMode()
+ {
+ regularUsername = SaveSystem.CurrentUser.Username; // k for now put user's username in here for the time being
+ SaveSystem.CurrentUser = SaveSystem.Users.FirstOrDefault(x => x.Username == "root"); // now their username is root
+
+ }
+
+ public static void LeaveKernelMode()
+ {
+ var user = SaveSystem.Users.FirstOrDefault(x => x.Username == regularUsername); //finds username
+ if (user == null)
+ throw new Exception("User not in root mode."); // fuck this means the user isnt root quick throw error
+ SaveSystem.CurrentUser = user;
+ }
+
+ //determines if you can disconnect from mud if there are no applications that currently need to
+ internal static bool CanRunOffline(Type method)
+ {
+ if (MudConnected)
+ return true;
+
+ foreach (var attr in method.GetCustomAttributes(false))
+ {
+ if (attr is MultiplayerOnlyAttribute)
+ return false;
+ }
+ return true;
+ }
+
+ //same as above but this time for methods
+ internal static bool CanRunOffline(MethodInfo method)
+ {
+ if (MudConnected)
+ return true;
+
+ foreach(var attr in method.GetCustomAttributes(false))
+ {
+ if (attr is MultiplayerOnlyAttribute)
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/ShiftOS_TheReturn/Localization.cs b/ShiftOS_TheReturn/Localization.cs
new file mode 100644
index 0000000..2c701c9
--- /dev/null
+++ b/ShiftOS_TheReturn/Localization.cs
@@ -0,0 +1,240 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Newtonsoft.Json;
+using ShiftOS.Objects.ShiftFS;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ShiftOS.Engine
+{
+ //define a whole bunch of things that are needed
+ public interface ILanguageProvider
+ {
+ List<string> GetJSONTranscripts();
+ void WriteDefaultTranscript();
+ void WriteTranscript();
+ string GetCurrentTranscript();
+ string[] GetAllLanguages();
+ }
+
+ public static class Localization
+ {
+ private static ILanguageProvider _provider = null;
+ private static string _languageid = null;
+
+ public static string[] GetAllLanguages()
+ {
+ if(_provider == null)
+ {
+ return JsonConvert.DeserializeObject<string[]>(Properties.Resources.languages); //collect all the languages availible
+ }
+ else
+ {
+ return _provider.GetAllLanguages(); //also collect all the languages avalible but from a specific provider this time
+ }
+ }
+
+ //if no local selected, english will be loaded
+ public static void SetupTHETRUEDefaultLocals()
+ {
+ if (_provider == null)
+ {
+ var lines = Properties.Resources.strings_en;
+ var path = "english.local";
+ Utils.WriteAllText(Paths.GetPath(path), lines);
+ }
+ else if (SaveSystem.CurrentSave == null)
+ {
+ var lines = Properties.Resources.strings_en;
+ var path = "english.local";
+ Utils.WriteAllText(Paths.GetPath(path), lines);
+ }
+ else
+ {
+ _provider.WriteTranscript();
+ }
+ }
+
+ // ignore this not really setup of default no no zone
+ public static void SetupDefaultLocals(string lines, string path)
+ {
+ Utils.WriteAllText(Paths.GetPath(path), lines);
+
+ }
+
+
+ // Takes in a string and parses localization blocks into text blocks in the current language.
+ // example: "{CODEPOINTS}: 0" will come out as "Codepoints: 0" if the current language is english
+ public static string Parse(string original)
+ {
+ return Parse(original, new Dictionary<string, string>());
+ }
+
+
+ public static string Parse(string original, Dictionary<string, string> replace)
+ {
+ Dictionary<string, string> localizationStrings = new Dictionary<string, string>();
+
+
+ try
+ {
+ localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(_provider.GetCurrentTranscript());
+ }
+ catch
+ {
+ localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(Utils.ReadAllText(Paths.GetPath("english.local"))); //if no provider fall back to english
+ }
+
+ foreach (var kv in localizationStrings)
+ {
+ original = original.Replace(kv.Key, kv.Value); // goes through and replaces all the localization blocks
+ }
+
+ List<string> orphaned = new List<string>();
+ if (Utils.FileExists("0:/dev_orphaned_lang.txt"))
+ {
+ orphaned = JsonConvert.DeserializeObject<List<string>>(Utils.ReadAllText("0:/dev_orphaned_lang.txt")); // if this file exists read from it and put in list orphaned
+ }
+
+
+ int start_index = 0;
+ int length = 0;
+ bool indexing = false;
+
+ foreach (var c in original)
+ {
+ // start paying attenion when you see a "{"
+ if (c == '{')
+ {
+ start_index = original.IndexOf(c);
+ indexing = true;
+ }
+
+ if (indexing == true)
+ {
+ // stop paying attention when you see a "}" after seeing a "{"
+ length++;
+ if (c == '}')
+ {
+ indexing = false;
+ string o = original.Substring(start_index, length);
+ if (!orphaned.Contains(o))
+ {
+ orphaned.Add(o);
+ }
+ start_index = 0;
+ length = 0;
+ }
+ }
+ }
+
+ if (orphaned.Count > 0)
+ {
+ Utils.WriteAllText("0:/dev_orphaned_lang.txt", JsonConvert.SerializeObject(orphaned, Formatting.Indented)); //format if from this txt file
+ }
+
+ //string original2 = Parse(original);
+
+ string usernameReplace = "";
+ string domainReplace = "";
+
+ // if the user has saved then store their username and systemname in these string variables please
+ if (SaveSystem.CurrentSave != null)
+ {
+ try
+ {
+ usernameReplace = SaveSystem.CurrentUser.Username;
+ }
+ catch
+ {
+ usernameReplace = "user";
+ }
+
+ try
+ {
+ domainReplace = SaveSystem.CurrentSave.SystemName;
+ }
+ catch
+ {
+ domainReplace = "system";
+ }
+
+ }
+
+ string namespaceReplace = "";
+ string commandReplace = "";
+
+ // if the user did a command in the terminal and it had a period in it then split it up into the part before the period and the part after and then store them into these two string variables please
+ if (TerminalBackend.latestCommmand != "" && TerminalBackend.latestCommmand.IndexOf('.') > -1)
+ {
+ namespaceReplace = TerminalBackend.latestCommmand.Split('.')[0];
+ commandReplace = TerminalBackend.latestCommmand.Split('.')[1];
+ }
+
+ // if you see these then replace them with what you need to
+ Dictionary<string, string> defaultReplace = new Dictionary<string, string>() {
+ {"%username", usernameReplace},
+ {"%domain", domainReplace},
+ {"%ns", namespaceReplace},
+ {"%cmd", commandReplace},
+ {"%cp", SaveSystem.CurrentSave?.Codepoints.ToString() },
+ };
+
+ // actually do the replacement
+ foreach (KeyValuePair<string, string> replacement in replace)
+ {
+ original = original.Replace(replacement.Key, Parse(replacement.Value));
+ }
+
+ // do the replacement but default
+ foreach (KeyValuePair<string, string> replacement in defaultReplace)
+ {
+ original = original.Replace(replacement.Key, replacement.Value);
+ }
+
+ return original; // returns the now replaced string
+ }
+
+ // a few things are defined here
+ public static void RegisterProvider(ILanguageProvider p)
+ {
+ _provider = p;
+ }
+
+ public static void SetLanguageID(string id)
+ {
+ _languageid = id;
+ }
+
+ public static string GetLanguageID()
+ {
+ return _languageid;
+ }
+ }
+}
diff --git a/ShiftOS_TheReturn/NotificationDaemon.cs b/ShiftOS_TheReturn/NotificationDaemon.cs
new file mode 100644
index 0000000..a90510a
--- /dev/null
+++ b/ShiftOS_TheReturn/NotificationDaemon.cs
@@ -0,0 +1,141 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using ShiftOS.Objects.ShiftFS;
+
+namespace ShiftOS.Engine
+{
+ public static class NotificationDaemon
+ {
+ //if the notifications file already exists then get them
+ public static Notification[] GetAllFromFile()
+ {
+ Notification[] notes = { };
+ if (Utils.FileExists(Paths.GetPath("notifications.dat")))
+ {
+ notes = JsonConvert.DeserializeObject<Notification[]>(Utils.ReadAllText(Paths.GetPath("notifications.dat")));
+ }
+ return notes;
+ }
+
+ //tells the computer how it likes it to be written in the file
+ internal static void WriteNotes(Notification[] notes)
+ {
+ Utils.WriteAllText(Paths.GetPath("notifications.dat"), JsonConvert.SerializeObject(notes, Formatting.Indented)); //"write it in there indented pls"
+ }
+
+ public static event Action<Notification> NotificationMade; //use this if you want to know when a notification has been made
+
+ public static void AddNotification(NotificationType note, object data)
+ {
+ var lst = new List<Notification>(GetAllFromFile()); //grabs all current notifications
+ lst.Add(new Engine.Notification(note, data)); //then adds the new one to the list
+ WriteNotes(lst.ToArray());
+ NotificationMade?.Invoke(lst[lst.Count - 1]); //says to the program that a notification has indeed been made
+ }
+
+ public static event Action NotificationRead;
+
+ //for every notification that there is, mark them as read
+ public static void MarkAllRead()
+ {
+ var notes = GetAllFromFile();
+ for (int i = 0; i < notes.Length; i++)
+ MarkRead(i);
+ }
+
+ //grabs list of notifcations and if the notification you want to mark as read actually exsists, then it assigns it as read
+ public static void MarkRead(int note)
+ {
+ var notes = GetAllFromFile();
+ if (note >= notes.Length || note < 0)
+ throw new ArgumentOutOfRangeException("note", new Exception("You cannot mark a notification that does not exist as read."));
+
+ notes[note].Read = true; //assigns the specific notification as read
+ WriteNotes(notes);
+ NotificationRead?.Invoke();
+ }
+
+ public static int GetUnreadCount() //use this if you want the unread notification count, but i think you probably already knew that
+ {
+ int c = 0;
+ foreach (var note in GetAllFromFile())
+ if (note.Read == false)
+ c++; //gahh I hate that programming language. //dont we all
+ return c;
+ }
+
+ }
+
+ //actually gives the proper data for the notification
+ public struct Notification
+ {
+ //defaults for all notificaions
+ public Notification(NotificationType t, object data)
+ {
+ Type = t;
+ Data = data;
+ Read = false;
+ Timestamp = DateTime.Now;
+ }
+
+ public bool Read { get; set; }
+ public NotificationType Type { get; set; }
+ public object Data { get; set; }
+ public DateTime Timestamp { get; set; }
+ }
+
+ //defines all the possible notificaions that can happen
+ public enum NotificationType
+ {
+ Generic = 0x00, //lets get generic
+ MemoReceived = 0x10,
+ MemoSent = 0x11,
+ DownloadStarted = 0x20,
+ DownloadComplete = 0x21,
+ CodepointsReceived = 0x30,
+ CodepointsSent = 0x31,
+ ShopPurchase = 0x40,
+ LegionInvite = 0x50,
+ LegionKick = 0x51,
+ LegionBan = 0x52,
+ ChatBan = 0x60,
+ MUDAnnouncement = 0x70,
+ MUDMaintenance = 0x71,
+ NewShiftOSUnstable = 0x72,
+ NewShiftOSStable = 0x73,
+ NewAppveyor = 0x74,
+ CriticalBugwatch = 0x75,
+ NewDeveloper = 0x76,
+ NewShiftOSVideo = 0x77,
+ NewShiftOSStream = 0x78,
+ SavePurge = 0x79,
+ }
+} \ No newline at end of file
diff --git a/ShiftOS_TheReturn/OutOfBoxExperience.cs b/ShiftOS_TheReturn/OutOfBoxExperience.cs
index 6ed9e49..eb8e61d 100644
--- a/ShiftOS_TheReturn/OutOfBoxExperience.cs
+++ b/ShiftOS_TheReturn/OutOfBoxExperience.cs
@@ -45,17 +45,17 @@ namespace ShiftOS.Engine
public static void Init(IOobe oobe)
{
- _oobe = oobe;
+ _oobe = oobe; // takes the oobe and makes it an IOobe
}
public static void Start(Save save)
{
-
+ //if its null then FUCK YOU DID THE WRONG THING
if (_oobe == null)
throw new InvalidOperationException("OOBE frontend not activated! This function can't be used! Please use OutOfBoxExperience.Init() passing an IOobe-implementing object to start the OOBE frontend.");
- _oobe.StartShowing(save);
+ _oobe.StartShowing(save); //tells the save data to start showing the oobe
}
@@ -64,7 +64,7 @@ namespace ShiftOS.Engine
{
Desktop.InvokeOnWorkerThread(new Action(() =>
{
- _oobe.PromptForLogin();
+ _oobe.PromptForLogin(); //prompts for login, what did you expect
}));
}
@@ -72,12 +72,13 @@ namespace ShiftOS.Engine
{
Desktop.InvokeOnWorkerThread(new Action(() =>
{
- _oobe.ShowSaveTransfer(save);
+ _oobe.ShowSaveTransfer(save); //triggers save transfer if not done already
}));
}
}
+ //triggers all the above events
public interface IOobe
{
void StartShowing(Save save);
diff --git a/ShiftOS_TheReturn/Paths.cs b/ShiftOS_TheReturn/Paths.cs
new file mode 100644
index 0000000..5b75ae6
--- /dev/null
+++ b/ShiftOS_TheReturn/Paths.cs
@@ -0,0 +1,276 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using static ShiftOS.Objects.ShiftFS.Utils;
+using ShiftOS.Objects.ShiftFS;
+using Newtonsoft.Json;
+using System.Threading;
+
+namespace ShiftOS.Engine
+{
+ /// <summary>
+ /// Management class for ShiftFS path variables.
+ /// </summary>
+ public static class Paths
+ {
+ /// <summary>
+ /// Initiate the path system.
+ /// </summary>
+ public static void Init()
+ {
+ Locations = new Dictionary<string, string>();
+ Locations.Add("root", "0:");
+
+ AddPath("root", "system");
+
+ AddPath("root", "home");
+ AddPath("home", "documents");
+ AddPath("home", "desktop");
+ AddPath("home", "pictures");
+
+
+ AddPath("system", "local");
+ AddPath("local", "english.local");
+ AddPath("local", "deutsch.local");
+ AddPath("local", "verbose.local");
+ AddPath("system", "data");
+ AddPath("system", "applauncher");
+ AddPath("data", "save.json");
+ AddPath("data", "user.dat");
+ AddPath("data", "notifications.dat");
+ AddPath("data", "skin");
+ AddPath("skin", "widgets.dat");
+ AddPath("system", "programs");
+ AddPath("system", "kernel.sft");
+ AddPath("system", "conf.sft");
+ AddPath("skin", "current");
+ AddPath("current", "skin.json");
+ AddPath("current", "images");
+
+ CheckPathExistence();
+
+ CreateAndMountSharedFolder();
+ }
+
+ /// <summary>
+ /// Returns all paths in an array of strings.
+ /// </summary>
+ /// <returns>The array</returns>
+ public static string[] GetAll()
+ {
+ List<string> strings = new List<string>();
+ foreach(var str in Locations)
+ {
+ strings.Add(str.Key + " = " + str.Value);
+ }
+ return strings.ToArray();
+
+ }
+
+ /// <summary>
+ /// Gets all full paths without their keynames.
+ /// </summary>
+ /// <returns>A string array representing all paths.</returns>
+ public static string[] GetAllWithoutKey()
+ {
+ List<string> strings = new List<string>();
+ foreach (var str in Locations)
+ {
+ strings.Add(str.Value);
+ }
+ return strings.ToArray();
+
+ }
+
+ /// <summary>
+ /// Get the full path using a path key.
+ /// </summary>
+ /// <param name="id">The path key (folder/filename) for the path.</param>
+ /// <returns>The full path.</returns>
+ public static string GetPath(string id)
+ {
+ return Locations[id];
+ }
+
+ /// <summary>
+ /// Checks all directories in the path system to see if they exist, and if not, creates them.
+ /// </summary>
+ private static void CheckPathExistence()
+ {
+ foreach(var path in Locations)
+ {
+ if (!path.Value.Contains(".") && path.Key != "classic")
+ {
+ if (!DirectoryExists(path.Value))
+ {
+ Console.WriteLine($"Writing directory: {path.Value.Replace(Locations["root"], "\\")}");
+ CreateDirectory(path.Value);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a <see cref="Dictionary{string, string}"/> representing all paths in the system.
+ /// </summary>
+ private static Dictionary<string, string> Locations { get; set; }
+
+ /// <summary>
+ /// Mounts the ShiftOS shared directory to 1:/, creating the directory if it does not exist.
+ /// </summary>
+ public static void CreateAndMountSharedFolder()
+ {
+ if (!System.IO.Directory.Exists(SharedFolder))
+ {
+ System.IO.Directory.CreateDirectory(SharedFolder);
+ }
+
+ var mount = new Directory();
+ mount.Name = "Shared";
+ Utils.Mount(JsonConvert.SerializeObject(mount));
+ ScanForDirectories(SharedFolder, 1);
+ //This event-based system allows us to sync the ramdisk from ShiftOS to the host OS.
+ Utils.DirectoryCreated += (dir) =>
+ {
+ try
+ {
+ if (dir.StartsWith("1:/"))
+ {
+ string real = dir.Replace("/", "\\").Replace("1:", SharedFolder);
+ if (!System.IO.Directory.Exists(real))
+ System.IO.Directory.CreateDirectory(real);
+ }
+ }
+ catch { }
+ };
+
+ Utils.DirectoryDeleted += (dir) =>
+ {
+ try
+ {
+ if (dir.StartsWith("1:/"))
+ {
+ string real = dir.Replace("/", "\\").Replace("1:", SharedFolder);
+ if (System.IO.Directory.Exists(real))
+ System.IO.Directory.Delete(real, true);
+ }
+ }
+ catch { }
+ };
+
+ Utils.FileWritten += (dir) =>
+ {
+ try
+ {
+ if (dir.StartsWith("1:/"))
+ {
+ string real = dir.Replace("/", "\\").Replace("1:", SharedFolder);
+ System.IO.File.WriteAllBytes(real, ReadAllBytes(dir));
+ }
+ }
+ catch { }
+ };
+
+ Utils.FileDeleted += (dir) =>
+ {
+ try
+ {
+ if (dir.StartsWith("1:/"))
+ {
+ string real = dir.Replace("/", "\\").Replace("1:", SharedFolder);
+ if (System.IO.File.Exists(real))
+ System.IO.File.Delete(real);
+ }
+ }
+ catch { }
+ };
+
+ //This thread will sync the ramdisk from the host OS to ShiftOS.
+ var t = new Thread(() =>
+ {
+ while (!SaveSystem.ShuttingDown)
+ {
+ Thread.Sleep(15000);
+ ScanForDirectories(SharedFolder, 1);
+ }
+ });
+ t.IsBackground = true;
+ t.Start();
+ }
+
+ private static void ScanForDirectories(string folder, int mount)
+ {
+ foreach (var file in System.IO.Directory.GetFiles(folder))
+ {
+ string mfsDir = file.Replace(SharedFolder, $"{mount}:").Replace("\\", "/");
+ if (!FileExists(mfsDir))
+ WriteAllBytes(mfsDir, System.IO.File.ReadAllBytes(file));
+ }
+ foreach (var directory in System.IO.Directory.GetDirectories(folder))
+ {
+ string mfsDir = directory.Replace(SharedFolder, $"{mount}:").Replace("\\", "/");
+ if(!DirectoryExists(mfsDir))
+ CreateDirectory(mfsDir);
+ ScanForDirectories(directory, mount);
+ }
+ }
+
+ /// <summary>
+ /// Gets the ShiftOS shared folder.
+ /// </summary>
+ public static string SharedFolder { get { return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\ShiftOS_Shared"; } }
+
+ /// <summary>
+ /// Gets the location of the ShiftOS.mfs file.
+ /// </summary>
+ public static string SaveFile { get { return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\ShiftOS.mfs"; } }
+
+ /// <summary>
+ /// Gets the path of the inner save file.
+ /// </summary>
+ [Obsolete("Not used.")]
+ public static string SaveFileInner { get { return Locations["save.json"]; } }
+
+ /// <summary>
+ /// Add a path to the system.
+ /// </summary>
+ /// <param name="parent">The path's parent directory.</param>
+ /// <param name="path">The filename for the path.</param>
+ public static void AddPath(string parent, string path)
+ {
+ Locations.Add(path, Locations[parent] + "/" + path);
+ }
+
+ public static string Translate(string path)
+ {
+ return Locations["root"] + path.Replace("\\", "/");
+ }
+ }
+}
diff --git a/ShiftOS_TheReturn/SaveSystem.cs b/ShiftOS_TheReturn/SaveSystem.cs
new file mode 100644
index 0000000..9c812c7
--- /dev/null
+++ b/ShiftOS_TheReturn/SaveSystem.cs
@@ -0,0 +1,565 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+//#define ONLINEMODE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.IO;
+using Newtonsoft.Json;
+using ShiftOS.Objects;
+using ShiftOS.Objects.ShiftFS;
+using oobe = ShiftOS.Engine.OutOfBoxExperience;
+using static System.Net.Mime.MediaTypeNames;
+
+namespace ShiftOS.Engine
+{
+ [Obsolete("Use the servers.conf file instead.")]
+ public class EngineConfig
+ {
+ public bool ConnectToMud = true;
+ public string MudDefaultIP = "dome.rol.im";
+ public int MudDefaultPort = 13370;
+ }
+
+ /// <summary>
+ /// Management class for the ShiftOS save system.
+ /// </summary>
+ public static class SaveSystem
+ {
+ /// <summary>
+ /// Boolean representing whether the system is shutting down.
+ /// </summary>
+ public static bool ShuttingDown = false;
+
+ /// <summary>
+ /// Gets or sets the current logged in client-side user.
+ /// </summary>
+ public static ClientSave CurrentUser { get; set; }
+
+ /// <summary>
+ /// Boolean representing whether the save system is ready to be used.
+ /// </summary>
+ public static bool Ready = false;
+
+ /// <summary>
+ /// Occurs before the save system connects to the ShiftOS Digital Society.
+ /// </summary>
+ public static event Action PreDigitalSocietyConnection;
+
+ /// <summary>
+ /// Gets or sets the current server-side save file.
+ /// </summary>
+ public static Save CurrentSave { get; set; }
+
+ /// <summary>
+ /// Start the entire ShiftOS engine.
+ /// </summary>
+ /// <param name="useDefaultUI">Whether ShiftOS should initiate it's Windows Forms front-end.</param>
+ public static void Begin(bool useDefaultUI = true)
+ {
+ AppDomain.CurrentDomain.UnhandledException += (o, a) =>
+ {
+ CrashHandler.Start((Exception)a.ExceptionObject);
+ };
+
+ if (!System.IO.File.Exists(Paths.SaveFile))
+ {
+ var root = new ShiftOS.Objects.ShiftFS.Directory();
+ root.Name = "System";
+ root.permissions = UserPermissions.Guest;
+ System.IO.File.WriteAllText(Paths.SaveFile, JsonConvert.SerializeObject(root));
+ }
+
+ if (Utils.Mounts.Count == 0)
+ Utils.Mount(System.IO.File.ReadAllText(Paths.SaveFile));
+ Paths.Init();
+
+ Localization.SetupTHETRUEDefaultLocals();
+ SkinEngine.Init();
+
+ TerminalBackend.OpenTerminal();
+
+ TerminalBackend.InStory = true;
+ var thread = new Thread(new ThreadStart(() =>
+ {
+ //Do not uncomment until I sort out the copyright stuff... - Michael
+ //AudioManager.Init();
+
+ var defaultConf = new EngineConfig();
+ if (System.IO.File.Exists("engineconfig.json"))
+ defaultConf = JsonConvert.DeserializeObject<EngineConfig>(System.IO.File.ReadAllText("engineconfig.json"));
+ else
+ {
+ System.IO.File.WriteAllText("engineconfig.json", JsonConvert.SerializeObject(defaultConf, Formatting.Indented));
+ }
+
+ Thread.Sleep(350);
+ Console.WriteLine("ShiftKernel v0.4.2");
+ Console.WriteLine("(MIT) DevX 2017, Very Little Rights Reserved");
+ Console.WriteLine("");
+ Console.WriteLine("THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR");
+ Console.WriteLine("IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,");
+ Console.WriteLine("FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE");
+ Console.WriteLine("AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER");
+ Console.WriteLine("LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,");
+ Console.WriteLine("OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE");
+ Console.WriteLine("SOFTWARE.");
+ Console.WriteLine("");
+ Thread.Sleep(250);
+ Console.WriteLine("[init] Kernel boot complete.");
+ Console.WriteLine("[sfs] Loading SFS driver v3");
+ Thread.Sleep(100);
+ Console.WriteLine("[sfs] 4096 blocks read.");
+ Console.WriteLine("[simpl-conf] Reading configuration files (global-3.conf)");
+
+ Console.WriteLine("[inetd] Connecting to network...");
+
+ Ready = false;
+
+ if (PreDigitalSocietyConnection != null)
+ {
+ PreDigitalSocietyConnection?.Invoke();
+
+ while (!Ready)
+ {
+ Thread.Sleep(10);
+ }
+ }
+
+ if (defaultConf.ConnectToMud == true)
+ {
+ bool guidReceived = false;
+ ServerManager.GUIDReceived += (str) =>
+ {
+ //Connection successful! Stop waiting!
+ guidReceived = true;
+ Console.WriteLine("[inetd] Connection successful.");
+ };
+
+ try
+ {
+
+ ServerManager.Initiate(UserConfig.Get().DigitalSocietyAddress, UserConfig.Get().DigitalSocietyPort);
+ //This haults the client until the connection is successful.
+ while (ServerManager.thisGuid == new Guid())
+ {
+ Thread.Sleep(10);
+ }
+ Console.WriteLine("[inetd] DHCP GUID recieved, finished setup");
+ FinishBootstrap();
+ }
+ catch (Exception ex)
+ {
+ //No errors, this never gets called.
+ Console.WriteLine("[inetd] SEVERE: " + ex.Message);
+ Thread.Sleep(3000);
+ ServerManager.StartLANServer();
+ while (ServerManager.thisGuid == new Guid())
+ {
+ Thread.Sleep(10);
+ }
+ Console.WriteLine("[inetd] DHCP GUID recieved, finished setup");
+ FinishBootstrap();
+ }
+ }
+ else
+ {
+ ServerManager.StartLANServer();
+ }
+
+ //Nothing happens past this point - but the client IS connected! It shouldn't be stuck in that while loop above.
+
+
+ }));
+ thread.IsBackground = true;
+ thread.Start();
+ }
+
+ /// <summary>
+ /// Finish bootstrapping the engine.
+ /// </summary>
+ private static void FinishBootstrap()
+ {
+ KernelWatchdog.Log("mud_handshake", "handshake successful: kernel watchdog access code is \"" + ServerManager.thisGuid.ToString() + "\"");
+
+ ServerMessageReceived savehandshake = null;
+
+ savehandshake = (msg) =>
+ {
+ if (msg.Name == "mud_savefile")
+ {
+ CurrentSave = JsonConvert.DeserializeObject<Save>(msg.Contents);
+ ServerManager.MessageReceived -= savehandshake;
+ }
+ else if (msg.Name == "mud_login_denied")
+ {
+ oobe.PromptForLogin();
+ ServerManager.MessageReceived -= savehandshake;
+ }
+ };
+ ServerManager.MessageReceived += savehandshake;
+
+
+ ReadSave();
+
+ while (CurrentSave == null)
+ {
+ Thread.Sleep(10);
+ }
+
+ Localization.SetupTHETRUEDefaultLocals();
+
+ Shiftorium.Init();
+
+ while (CurrentSave.StoryPosition < 1)
+ {
+ Thread.Sleep(10);
+ }
+
+ Thread.Sleep(75);
+
+ Thread.Sleep(50);
+ Console.WriteLine("[usr-man] Accepting logins on local tty 1.");
+
+ Sysname:
+ bool waitingForNewSysName = false;
+ bool gobacktosysname = false;
+
+ if (string.IsNullOrWhiteSpace(CurrentSave.SystemName))
+ {
+ Infobox.PromptText("Enter a system name", "Your system does not have a name. All systems within the digital society must have a name. Please enter one.", (name) =>
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ Infobox.Show("Invalid name", "Please enter a valid name.", () =>
+ {
+ gobacktosysname = true;
+ waitingForNewSysName = false;
+ });
+ else if (name.Length < 5)
+ Infobox.Show("Value too small.", "Your system name must have at least 5 characters in it.", () =>
+ {
+ gobacktosysname = true;
+ waitingForNewSysName = false;
+ });
+ else
+ {
+ CurrentSave.SystemName = name;
+ if (!string.IsNullOrWhiteSpace(CurrentSave.UniteAuthToken))
+ {
+ var unite = new Unite.UniteClient("http://getshiftos.ml", CurrentSave.UniteAuthToken);
+ unite.SetSysName(name);
+ }
+ SaveSystem.SaveGame();
+ gobacktosysname = false;
+ waitingForNewSysName = false;
+ }
+ });
+
+
+ }
+
+ while (waitingForNewSysName)
+ {
+ Thread.Sleep(10);
+ }
+
+ if (gobacktosysname)
+ {
+ goto Sysname;
+ }
+
+ if (CurrentSave.Users == null)
+ CurrentSave.Users = new List<ClientSave>();
+
+
+ if (CurrentSave.Users.Count == 0)
+ {
+ CurrentSave.Users.Add(new ClientSave
+ {
+ Username = "root",
+ Password = "",
+ Permissions = UserPermissions.Root
+ });
+ Console.WriteLine("[usr-man] WARN: No users found. Creating new user with username \"root\", with no password.");
+ }
+ TerminalBackend.InStory = false;
+
+ TerminalBackend.PrefixEnabled = false;
+
+ if (LoginManager.ShouldUseGUILogin)
+ {
+ Action<ClientSave> Completed = null;
+ Completed += (user) =>
+ {
+ CurrentUser = user;
+ LoginManager.LoginComplete -= Completed;
+ };
+ LoginManager.LoginComplete += Completed;
+ Desktop.InvokeOnWorkerThread(() =>
+ {
+ LoginManager.PromptForLogin();
+ });
+ while (CurrentUser == null)
+ {
+ Thread.Sleep(10);
+ }
+ }
+ else
+ {
+
+ Login:
+ string username = "";
+ int progress = 0;
+ bool goback = false;
+ TextSentEventHandler ev = null;
+ ev = (text) =>
+ {
+ if (progress == 0)
+ {
+ string loginstr = CurrentSave.SystemName + " login: ";
+ string getuser = text.Remove(0, loginstr.Length);
+ if (!string.IsNullOrWhiteSpace(getuser))
+ {
+ if (CurrentSave.Users.FirstOrDefault(x => x.Username == getuser) == null)
+ {
+ Console.WriteLine("User not found.");
+ goback = true;
+ progress++;
+ TerminalBackend.TextSent -= ev;
+ return;
+ }
+ username = getuser;
+ progress++;
+ }
+ else
+ {
+ Console.WriteLine("Username not provided.");
+ TerminalBackend.TextSent -= ev;
+ goback = true;
+ progress++;
+ }
+ }
+ else if (progress == 1)
+ {
+ string passwordstr = "password: ";
+ string getpass = text.Remove(0, passwordstr.Length);
+ var user = CurrentSave.Users.FirstOrDefault(x => x.Username == username);
+ if (user.Password == getpass)
+ {
+ Console.WriteLine("Welcome to ShiftOS.");
+ CurrentUser = user;
+ Thread.Sleep(2000);
+ progress++;
+ }
+ else
+ {
+ Console.WriteLine("Access denied.");
+ goback = true;
+ progress++;
+ }
+ TerminalBackend.TextSent -= ev;
+ }
+ };
+ TerminalBackend.TextSent += ev;
+
+ Console.Write(CurrentSave.SystemName + " login: ");
+ while (progress == 0)
+ {
+ Thread.Sleep(10);
+ }
+ if (goback)
+ goto Login;
+ Console.Write("password: ");
+ while (progress == 1)
+ Thread.Sleep(10);
+ if (goback)
+ goto Login;
+ }
+ TerminalBackend.PrefixEnabled = true;
+ Shiftorium.LogOrphanedUpgrades = true;
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ ShiftOS.Engine.Scripting.LuaInterpreter.RunSft(Paths.GetPath("kernel.sft"));
+ }));
+
+
+ Desktop.InvokeOnWorkerThread(new Action(() => Desktop.PopulateAppLauncher()));
+ GameReady?.Invoke();
+ }
+
+ /// <summary>
+ /// Delegate type for events with no caller objects or event arguments. You can use the () => {...} (C#) lambda expression with this delegate
+ /// </summary>
+ public delegate void EmptyEventHandler();
+
+ /// <summary>
+ /// Gets a list of all client-side users.
+ /// </summary>
+ public static List<ClientSave> Users
+ {
+ get
+ {
+ return CurrentSave.Users;
+ }
+ }
+
+ /// <summary>
+ /// Occurs when the engine is loaded and the game can take over.
+ /// </summary>
+ public static event EmptyEventHandler GameReady;
+
+ /// <summary>
+ /// Deducts a set amount of Codepoints from the save file... and sends them to a place where they'll never be seen again.
+ /// </summary>
+ /// <param name="amount">The amount of Codepoints to deduct.</param>
+ public static void TransferCodepointsToVoid(long amount)
+ {
+ if (amount < 0)
+ throw new ArgumentOutOfRangeException("We see what you did there. Trying to pull Codepoints from the void? That won't work.");
+ CurrentSave.Codepoints -= amount;
+ NotificationDaemon.AddNotification(NotificationType.CodepointsSent, amount);
+ }
+
+ /// <summary>
+ /// Restarts the game.
+ /// </summary>
+ public static void Restart()
+ {
+ TerminalBackend.InvokeCommand("sos.shutdown");
+ System.Windows.Forms.Application.Restart();
+ }
+
+ /// <summary>
+ /// Requests the save file from the server. If authentication fails, this will cause the user to be prompted for their website login and a new save will be created if none is associated with the login.
+ /// </summary>
+ public static void ReadSave()
+ {
+ //Migrate old saves.
+ if(System.IO.Directory.Exists("C:\\ShiftOS2"))
+ {
+ Console.WriteLine("Old save detected. Migrating filesystem to MFS...");
+ foreach (string file in System.IO.Directory.EnumerateDirectories("C:\\ShiftOS2")
+.Select(d => new DirectoryInfo(d).FullName))
+ {
+ if(!Utils.DirectoryExists(file.Replace("C:\\ShiftOS2\\", "0:/").Replace("\\", "/")))
+ Utils.CreateDirectory(file.Replace("C:\\ShiftOS2\\", "0:/").Replace("\\", "/"));
+ }
+ foreach (string file in System.IO.Directory.EnumerateFiles("C:\\ShiftOS2"))
+ {
+
+ string rfile = Path.GetFileName(file);
+ Utils.WriteAllBytes(file.Replace("C:\\ShiftOS2\\", "0:/").Replace("\\", "/"), System.IO.File.ReadAllBytes(file));
+ Console.WriteLine("Exported file " + file);
+ }
+
+ }
+
+
+ if (Utils.FileExists(Paths.SaveFileInner))
+ {
+ oobe.ShowSaveTransfer(JsonConvert.DeserializeObject<Save>(Utils.ReadAllText(Paths.SaveFileInner)));
+ }
+ else
+ {
+ if (Utils.FileExists(Paths.GetPath("user.dat")))
+ {
+ string token = Utils.ReadAllText(Paths.GetPath("user.dat"));
+
+ ServerManager.SendMessage("mud_token_login", token);
+ }
+ else
+ {
+ NewSave();
+ }
+ }
+
+ }
+
+ /// <summary>
+ /// Creates a new save, starting the Out Of Box Experience (OOBE).
+ /// </summary>
+ public static void NewSave()
+ {
+ AppearanceManager.Invoke(new Action(() =>
+ {
+ CurrentSave = new Save();
+ CurrentSave.Codepoints = 0;
+ CurrentSave.Upgrades = new Dictionary<string, bool>();
+ Shiftorium.Init();
+ oobe.Start(CurrentSave);
+ }));
+ }
+
+ /// <summary>
+ /// Saves the game to the server, updating website stats if possible.
+ /// </summary>
+ public static void SaveGame()
+ {
+ if(!Shiftorium.Silent)
+ Console.WriteLine("");
+ if(!Shiftorium.Silent)
+ Console.Write("{SE_SAVING}... ");
+ if (SaveSystem.CurrentSave != null)
+ {
+ Utils.WriteAllText(Paths.GetPath("user.dat"), CurrentSave.UniteAuthToken);
+ ServerManager.SendMessage("mud_save", JsonConvert.SerializeObject(CurrentSave, Formatting.Indented));
+ }
+ if (!Shiftorium.Silent)
+ Console.WriteLine(" ...{DONE}.");
+ System.IO.File.WriteAllText(Paths.SaveFile, Utils.ExportMount(0));
+ }
+
+ /// <summary>
+ /// Transfers codepoints from an arbitrary character to the save file.
+ /// </summary>
+ /// <param name="who">The character name</param>
+ /// <param name="amount">The amount of Codepoints.</param>
+ public static void TransferCodepointsFrom(string who, long amount)
+ {
+ if (amount < 0)
+ throw new ArgumentOutOfRangeException("We see what you did there... You can't just give a fake character Codepoints like that. It's better if you transfer them to the void.");
+ NotificationDaemon.AddNotification(NotificationType.CodepointsReceived, amount);
+ CurrentSave.Codepoints += amount;
+ }
+ }
+
+ /// <summary>
+ /// Delegate for handling Terminal text input.
+ /// </summary>
+ /// <param name="text">The text inputted by the user (including prompt text).</param>
+ public delegate void TextSentEventHandler(string text);
+
+ /// <summary>
+ /// Denotes that this Terminal command or namespace is for developers.
+ /// </summary>
+ public class DeveloperAttribute : Attribute
+ {
+
+ }
+}
diff --git a/ShiftOS_TheReturn/Scripting.cs b/ShiftOS_TheReturn/Scripting.cs
new file mode 100644
index 0000000..dd5acfd
--- /dev/null
+++ b/ShiftOS_TheReturn/Scripting.cs
@@ -0,0 +1,716 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reflection;
+using ShiftOS.Objects.ShiftFS;
+using DynamicLua;
+using System.Dynamic;
+using Newtonsoft.Json;
+using System.Windows.Forms;
+using System.Threading;
+using System.Net;
+
+namespace ShiftOS.Engine.Scripting
+{
+ /// <summary>
+ /// Brings some C# goodies to the Lua system.
+ /// </summary>
+ [Exposed("strutils")]
+ public class StringUtils
+ {
+ /// <summary>
+ /// Checks if a string ends with a specified string.
+ /// </summary>
+ /// <param name="operand">The string to operate on</param>
+ /// <param name="value">The string to check for</param>
+ /// <returns>Whether <paramref name="operand"/> ends with <paramref name="value"/>.</returns>
+ public bool endswith(string operand, string value)
+ {
+ return operand.EndsWith(value);
+ }
+
+
+ /// <summary>
+ /// Checks if a string starts with a specified string.
+ /// </summary>
+ /// <param name="operand">The string to operate on</param>
+ /// <param name="value">The string to check for</param>
+ /// <returns>Whether <paramref name="operand"/> starts with <paramref name="value"/>.</returns>
+ public bool startswith(string operand, string value)
+ {
+ return operand.StartsWith(value);
+ }
+
+ /// <summary>
+ /// Checks if a string contains a specified string.
+ /// </summary>
+ /// <param name="operand">The string to operate on</param>
+ /// <param name="value">The string to check for</param>
+ /// <returns>Whether <paramref name="operand"/> contains <paramref name="value"/>.</returns>
+ public bool contains(string operand, string value)
+ {
+ return operand.Contains(value);
+ }
+ }
+
+ /// <summary>
+ /// DynamicLua wrapper for the ShiftOS engine.
+ /// </summary>
+ public class LuaInterpreter
+ {
+ /// <summary>
+ /// The DynamicLua backend.
+ /// </summary>
+ public dynamic Lua = new DynamicLua.DynamicLua();
+
+ /// <summary>
+ /// Boolean representing whether the script is running.
+ /// </summary>
+ public bool Running = true;
+
+ /// <summary>
+ /// Static constructor for the <see cref="LuaInterpreter"/> class.
+ /// </summary>
+ static LuaInterpreter()
+ {
+ ServerManager.MessageReceived += (msg) =>
+ {
+ if (msg.Name == "run")
+ {
+ TerminalBackend.PrefixEnabled = false;
+ var cntnts = JsonConvert.DeserializeObject<dynamic>(msg.Contents);
+ var interp = new LuaInterpreter();
+ Desktop.InvokeOnWorkerThread(() =>
+ {
+ interp.Execute(cntnts.script.ToString());
+
+ });
+ TerminalBackend.PrefixEnabled = true;
+ TerminalBackend.PrintPrompt();
+ }
+ };
+ }
+
+ /// <summary>
+ /// Create a .SFT representation of a Lua script.
+ /// </summary>
+ /// <param name="lua">The Lua code to convert</param>
+ /// <returns>Base64 SFT representation.</returns>
+ public static string CreateSft(string lua)
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(lua);
+ return Convert.ToBase64String(bytes);
+ }
+
+ /// <summary>
+ /// Run a compressed .SFT file as a lua script.
+ /// </summary>
+ /// <param name="sft">The .sft file to run.</param>
+ public static void RunSft(string sft)
+ {
+ if (Utils.FileExists(sft))
+ {
+ try
+ {
+ string b64 = Utils.ReadAllText(sft);
+ byte[] bytes = Convert.FromBase64String(b64);
+ string lua = Encoding.UTF8.GetString(bytes);
+ CurrentDirectory = sft.Replace(Utils.GetFileInfo(sft).Name, "");
+ new LuaInterpreter().Execute(lua);
+ }
+ catch
+ {
+ Infobox.Show("Invalid binary.", "This file is not a valid ShiftOS binary executable.");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Get the current working directory of the script.
+ /// </summary>
+ public static string CurrentDirectory { get; private set; }
+
+ /// <summary>
+ /// Creates a new instance of the <see cref="LuaInterpreter"/> class.
+ /// </summary>
+ public LuaInterpreter()
+ {
+ Lua(@"function totable(clrlist)
+ local t = {}
+ local it = clrlist:GetEnumerator()
+ while it:MoveNext() do
+ t[#t+1] = it.Current
+ end
+ return t
+end");
+
+ SetupAPIs();
+ Application.ApplicationExit += (o, a) =>
+ {
+ Running = false;
+ };
+ }
+
+ /// <summary>
+ /// Scans the engine, frontend, and all mods for Lua-exposed classes and functions.
+ /// </summary>
+ public void SetupAPIs()
+ {
+ Lua.currentdir = (string.IsNullOrWhiteSpace(CurrentDirectory)) ? "0:" : CurrentDirectory;
+ Lua.random = new Func<int, int, int>((min, max) =>
+ {
+ return new Random().Next(min, max);
+ });
+ Lua.registerEvent = new Action<string, Action<object>>((eventName, callback) =>
+ {
+ LuaEvent += (e, s) =>
+ {
+ if (e == eventName)
+ try
+ {
+ callback?.Invoke(s);
+ }
+ catch(Exception ex)
+ {
+ Infobox.Show("Event propagation error.", "An error occurred while propagating the " + eventName + " event. " + ex.Message);
+ }
+ };
+ });
+ //This temporary proxy() method will be used by the API prober.
+ Lua.proxy = new Func<string, dynamic>((objName) =>
+ {
+ foreach (var f in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ if (f.EndsWith(".exe") || f.EndsWith(".dll"))
+ {
+ try
+ {
+
+ var asm = Assembly.LoadFile(f);
+ foreach (var type in asm.GetTypes())
+ {
+ if (type.Name == objName)
+ {
+ dynamic dynObj = Activator.CreateInstance(type);
+ return dynObj;
+ }
+
+ }
+ }
+ catch { }
+ }
+ }
+ throw new Exception("{CLASS_NOT_FOUND}");
+ });
+
+ foreach (var f in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ if (f.EndsWith(".exe") || f.EndsWith(".dll"))
+ {
+ try
+ {
+ var thisasm = Assembly.LoadFile(f);
+ foreach (var type in thisasm.GetTypes())
+ {
+ foreach (var attr in type.GetCustomAttributes(false))
+ {
+ if (attr is ExposedAttribute)
+ {
+ var eattr = attr as ExposedAttribute;
+ Lua($"{eattr.Name} = proxy(\"{type.Name}\")");
+ }
+ }
+ }
+ }
+ catch
+ {
+
+ }
+ }
+ }
+ //Now we can null out the proxy() method as it can cause security risks.
+ Lua.isRunning = new Func<bool>(() => { return this.Running; });
+ Lua.proxy = null;
+ Lua.invokeOnWorkerThread = new Action<string>((func) =>
+ {
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ Lua(func + "()");
+ }));
+ });
+ Lua.invokeOnNewThread = new Action<string>((func) =>
+ {
+ var t = new Thread(new ThreadStart(() =>
+ {
+ Lua(func + "()");
+ }));
+ t.IsBackground = true;
+ t.Start();
+ });
+ Lua.includeScript = new Action<string>((file) =>
+ {
+ if (!Utils.FileExists(file))
+ throw new ArgumentException("File does not exist.");
+
+ if (!file.EndsWith(".lua"))
+ throw new ArgumentException("File is not a lua file.");
+
+ Lua(Utils.ReadAllText(file));
+ });
+ }
+
+ /// <summary>
+ /// Executes the specified file as an uncompressed Lua script.
+ /// </summary>
+ /// <param name="file">The file to execute.</param>
+ public void ExecuteFile(string file)
+ {
+ if (Utils.FileExists(file))
+ {
+ CurrentDirectory = file.Replace(Utils.GetFileInfo(file).Name, "");
+ Execute(Utils.ReadAllText(file));
+ }
+ else
+ {
+ throw new Exception("The file \"" + file + "\" was not found on the system.");
+ }
+ }
+
+ /// <summary>
+ /// Executes the specified string as a Lua script.
+ /// </summary>
+ /// <param name="lua">The Lua code to execute.</param>
+ public void Execute(string lua)
+ {
+ try
+ {
+ Console.WriteLine("");
+ Lua(lua);
+ Console.WriteLine($"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ ");
+ }
+ catch (Exception e)
+ {
+ Infobox.Show("Script error", $@"Script threw {e.GetType().Name}:
+
+{e.Message}");
+ }
+ }
+
+ /// <summary>
+ /// Occurs when a Lua event is fired by C#.
+ /// </summary>
+ private static event Action<string, object> LuaEvent;
+
+ /// <summary>
+ /// Raises a Lua event with the specified name and caller object.
+ /// </summary>
+ /// <param name="eventName">The name of the event. Scripts use this to check what type of event occurred.</param>
+ /// <param name="caller">The caller of the event. Scripts can use this to check if they should handle this event.</param>
+ public static void RaiseEvent(string eventName, object caller)
+ {
+ LuaEvent?.Invoke(eventName, caller);
+ }
+ }
+
+ /// <summary>
+ /// Lua functions for .sft files.
+ /// </summary>
+ [Exposed("sft")]
+ public class SFTFunctions
+ {
+ /// <summary>
+ /// Make a .sft file from a lua code string
+ /// </summary>
+ /// <param name="lua">The Lua code</param>
+ /// <returns>The resulting .sft string</returns>
+ public string make(string lua)
+ {
+ return LuaInterpreter.CreateSft(lua);
+ }
+
+ /// <summary>
+ /// Make a .sft string and save to a specified file.
+ /// </summary>
+ /// <param name="lua">The Lua code to compress</param>
+ /// <param name="outpath">The path to save the compressed .sft file to.</param>
+ public void makefile(string lua, string outpath)
+ {
+ Utils.WriteAllText(outpath, make(lua));
+ }
+
+ /// <summary>
+ /// Run a compressed .sft file in the <see cref="LuaInterpreter"/>.
+ /// </summary>
+ /// <param name="inpath">The .sft file to run.</param>
+ public void run(string inpath)
+ {
+ LuaInterpreter.RunSft(inpath);
+ }
+
+ /// <summary>
+ /// Reads the specified .sft file and decompresses to it's Lua form.
+ /// </summary>
+ /// <param name="sft">The .sft file to uncompress</param>
+ /// <returns>The resulting Lua code.</returns>
+ public string unmake(string sft)
+ {
+ if (Utils.FileExists(sft))
+ {
+ string b64 = Utils.ReadAllText(sft);
+ byte[] bytes = Convert.FromBase64String(b64);
+ string lua = Encoding.UTF8.GetString(bytes);
+ return lua;
+ }
+ return "";
+ }
+ }
+
+ /// <summary>
+ /// Network functions for Lua.
+ /// </summary>
+ [Exposed("net")]
+ public class NetFunctions
+ {
+ /// <summary>
+ /// Submit a GET request to the specified URL.
+ /// </summary>
+ /// <param name="url">The URL to open</param>
+ /// <returns>The result from the server</returns>
+ public string get(string url)
+ {
+ return new WebClient().DownloadString(url);
+ }
+
+ }
+
+ /// <summary>
+ /// Console functions for Lua.
+ /// </summary>
+ [Exposed("console")]
+ public class ConsoleFunctions
+ {
+ /// <summary>
+ /// Write text to the console.
+ /// </summary>
+ /// <param name="text">The text to write.</param>
+ public void write(dynamic text)
+ {
+ Console.Write(text.ToString());
+ }
+
+ /// <summary>
+ /// Write text to the console, followed by a new line.
+ /// </summary>
+ /// <param name="text">The text to write.</param>
+ public void writeLine(dynamic text)
+ {
+ Console.WriteLine(text.ToString());
+ }
+ }
+
+ /// <summary>
+ /// The main ShiftOS API.
+ /// </summary>
+ [Exposed("sos")]
+ public class SystemFunctions
+ {
+ /// <summary>
+ /// Retrieves the user's Codepoints from the save file.
+ /// </summary>
+ /// <returns>The user's Codepoints.</returns>
+ public long getCodepoints() { return SaveSystem.CurrentSave.Codepoints; }
+
+ /// <summary>
+ /// Run a command in the Terminal.
+ /// </summary>
+ /// <param name="cmd">The command to run, using regular ShiftOS syntax.</param>
+ /// <returns>Whether the command was found and ran.</returns>
+ public bool runCommand(string cmd)
+ {
+ var args = TerminalBackend.GetArgs(ref cmd);
+
+ return TerminalBackend.RunClient(cmd, args);
+ }
+
+ /// <summary>
+ /// Adds the specified amount of Codepoints to the save flie.
+ /// </summary>
+ /// <param name="cp">The codepoints to add.</param>
+ public void addCodepoints(int cp)
+ {
+ if (cp > 100 || cp <= 0)
+ {
+ throw new Exception("Value 'cp' must be at or below 100, and above 0.");
+ }
+ else
+ {
+ SaveSystem.CurrentSave.Codepoints += cp;
+ SaveSystem.SaveGame();
+ }
+ }
+ }
+
+ /// <summary>
+ /// User information API.
+ /// </summary>
+ [Exposed("userinfo")]
+ public class UserInfoFunctions
+ {
+ /// <summary>
+ /// Gets the user name of the currently logged in user.
+ /// </summary>
+ /// <returns>The user's username.</returns>
+ public string getUsername()
+ {
+ return SaveSystem.CurrentUser.Username;
+ }
+
+ /// <summary>
+ /// Retrieves the user's system name.
+ /// </summary>
+ /// <returns>The user's system name.</returns>
+ public string getSysname()
+ {
+ return SaveSystem.CurrentSave.SystemName;
+ }
+
+ /// <summary>
+ /// Gets the user's ShiftOS email (username@sysname).
+ /// </summary>
+ /// <returns>The user's email.</returns>
+ public string getEmail()
+ {
+ return getUsername() + "@" + getSysname();
+ }
+ }
+
+ /// <summary>
+ /// Infobox API for Lua.
+ /// </summary>
+ [Exposed("infobox")]
+ public class InfoboxFunctions
+ {
+ /// <summary>
+ /// Show a message to the user in an Infobox.
+ /// </summary>
+ /// <param name="title">The title of the Infobox</param>
+ /// <param name="message">The infobox's message</param>
+ /// <param name="callback">A function to run when the user clicks "OK"</param>
+ public void show(string title, string message, Action callback = null)
+ {
+ Infobox.Show(title, message, callback);
+ }
+
+ /// <summary>
+ /// Ask a simple yes/no question to the user using an Infobox.
+ /// </summary>
+ /// <param name="title">The title of the Infobox</param>
+ /// <param name="message">The infobox's message</param>
+ /// <param name="callback">A function to run when they choose an option. The boolean argument will be true if the user clicks Yes, and false if they click No.</param>
+ public void question(string title, string message, Action<bool> callback)
+ {
+ Infobox.PromptYesNo(title, message, callback);
+ }
+
+ /// <summary>
+ /// Prompt the user for text using an Infobox.
+ /// </summary>
+ /// <param name="title">The infobox's title</param>
+ /// <param name="message">The infobox's message</param>
+ /// <param name="callback">A function to run when the user clicks "OK". The string value is the text entered by the user.</param>
+ /// <param name="isPassword">Whether the text box should hide its characters as if it were a password box.</param>
+ public void input(string title, string message, Action<string> callback, bool isPassword = false)
+ {
+ Infobox.PromptText(title, message, callback, isPassword);
+ }
+ }
+
+ /// <summary>
+ /// File Skimmer API for Lua.
+ /// </summary>
+ [Exposed("fileskimmer")]
+ public class FileSkimmerFunctions
+ {
+ /// <summary>
+ /// Opens a File Skimmer "Open File" dialog.
+ /// </summary>
+ /// <param name="extensions">Semicolon-separated list of file extensions that the opener should let through the filter.</param>
+ /// <param name="callback">Function to be called when the user chooses a file. The string value is the file's path.</param>
+ public void openFile(string extensions, Action<string> callback)
+ {
+ FileSkimmerBackend.GetFile(extensions.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries), FileOpenerStyle.Open, callback);
+ }
+
+ /// <summary>
+ /// Opens a File Skimmer "Save File" dialog.
+ /// </summary>
+ /// <param name="extensions">Semicolon-separated list of file extensions that the opener should let through the filter.</param>
+ /// <param name="callback">Function to be called when the user chooses a file. The string value is the file's path.</param>
+ public void saveFile(string extensions, Action<string> callback)
+ {
+ FileSkimmerBackend.GetFile(extensions.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries), FileOpenerStyle.Save, callback);
+ }
+ }
+
+ /// <summary>
+ /// ShiftFS API for Lua.
+ /// </summary>
+ [Exposed("fs")]
+ public class ShiftFSFunctions
+ {
+ /// <summary>
+ /// Read all text in a file to a string.
+ /// </summary>
+ /// <param name="path">The file path to read</param>
+ /// <returns>The string containing the file's contents.</returns>
+ public string readAllText(string path)
+ {
+ return Utils.ReadAllText(path);
+ }
+
+ /// <summary>
+ /// Copy a file from one place to another.
+ /// </summary>
+ /// <param name="i">The source file</param>
+ /// <param name="o">The destination path</param>
+ public void copy(string i, string o)
+ {
+ Utils.WriteAllBytes(o, Utils.ReadAllBytes(i));
+ }
+
+ /// <summary>
+ /// Gets all files in the specified directory.
+ /// </summary>
+ /// <param name="dir">The directory to search</param>
+ /// <returns>A string array containing all file paths in the directory.</returns>
+ public string[] getFiles(string dir)
+ {
+ return Utils.GetFiles(dir);
+ }
+
+ /// <summary>
+ /// Gets all directories inside a directory.
+ /// </summary>
+ /// <param name="dir">The directory to search</param>
+ /// <returns>A string array containing all directory paths in the directory.</returns>
+ public string[] getDirectories(string dir)
+ {
+ return Utils.GetDirectories(dir);
+ }
+
+ /// <summary>
+ /// Read the binary contents of a file to a <see cref="byte"/> array.
+ /// </summary>
+ /// <param name="path">The file path to read.</param>
+ /// <returns>The resulting byte array.</returns>
+ public byte[] readAllBytes(string path)
+ {
+ return Utils.ReadAllBytes(path);
+ }
+
+ /// <summary>
+ /// Writes the specified text to a file.
+ /// </summary>
+ /// <param name="path">The file path</param>
+ /// <param name="contents">The text to write</param>
+ public void writeAllText(string path, string contents)
+ {
+ Utils.WriteAllText(path, contents);
+ }
+
+ /// <summary>
+ /// Writes the specified binary data to a file.
+ /// </summary>
+ /// <param name="path">The file path.</param>
+ /// <param name="contents">The binary data</param>
+ public void writeAllBytes(string path, byte[] contents)
+ {
+ Utils.WriteAllBytes(path, contents);
+ }
+
+ /// <summary>
+ /// Determines whether the specified path exists and is a file.
+ /// </summary>
+ /// <param name="path">The path to search</param>
+ /// <returns>The result of the search.</returns>
+ public bool fileExists(string path)
+ {
+ return Utils.FileExists(path);
+ }
+
+ /// <summary>
+ /// Determines whether the specified path exists and is a directory.
+ /// </summary>
+ /// <param name="path">The path to search</param>
+ /// <returns>The result of the search.</returns>
+ public bool directoryExists(string path)
+ {
+ return Utils.DirectoryExists(path);
+ }
+
+ /// <summary>
+ /// Deletes the file/directory at the specified path.
+ /// </summary>
+ /// <param name="path">The path to delete</param>
+ public void delete(string path)
+ {
+ Utils.Delete(path);
+ }
+
+ /// <summary>
+ /// Creates a new directory at the specified path.
+ /// </summary>
+ /// <param name="path">The path to create</param>
+ public void createDirectory(string path)
+ {
+ Utils.CreateDirectory(path);
+ }
+ }
+
+ /// <summary>
+ /// Marks the specified class as a Lua API object.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+ public class ExposedAttribute : Attribute
+ {
+ /// <summary>
+ /// If applied to a non-static class, the ShiftOS engine will see this class as a Lua object of the specified name.
+ /// </summary>
+ /// <param name="name">The name of the Lua object</param>
+ public ExposedAttribute(string name)
+ {
+ Name = name;
+ }
+
+ /// <summary>
+ /// The API object's name
+ /// </summary>
+ public string Name { get; private set; }
+ }
+}
diff --git a/ShiftOS_TheReturn/ServerManager.cs b/ShiftOS_TheReturn/ServerManager.cs
new file mode 100644
index 0000000..95e86e9
--- /dev/null
+++ b/ShiftOS_TheReturn/ServerManager.cs
@@ -0,0 +1,313 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ShiftOS.Objects;
+using NetSockets;
+using System.Windows.Forms;
+using System.Threading;
+using ShiftOS;
+using static ShiftOS.Engine.SaveSystem;
+using Newtonsoft.Json;
+using System.Net.Sockets;
+using System.Diagnostics;
+
+namespace ShiftOS.Engine
+{
+ /// <summary>
+ /// Digital Society connection management class.
+ /// </summary>
+ public static class ServerManager
+ {
+ /// <summary>
+ /// Print connection diagnostic information.
+ /// </summary>
+ public static void PrintDiagnostics()
+ {
+ Console.WriteLine($@"{{CLIENT_DIAGNOSTICS}}
+
+{{GUID}}: {thisGuid}
+Ping: {ServerManager.DigitalSocietyPing} ms
+{{CLIENT_DATA}}:
+
+{JsonConvert.SerializeObject(client, Formatting.Indented)}");
+ }
+
+ /// <summary>
+ /// Gets the unique identifier for this Digital Society connection. This can be used for peer-to-peer communication between two clients.
+ /// </summary>
+ public static Guid thisGuid { get; private set; }
+
+ /// <summary>
+ /// Gets the underlying NetSockets client for this connection.
+ /// </summary>
+ public static NetObjectClient client { get; private set; }
+
+
+ private static bool UserDisconnect = false;
+
+ /// <summary>
+ /// Gets or sets the server response time for the last request made by this client.
+ /// </summary>
+ public static long DigitalSocietyPing
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Disconnect from the digital society intentionally.
+ /// </summary>
+ public static void Disconnect()
+ {
+ UserDisconnect = true;
+ if (client != null)
+ {
+ client.Disconnect();
+ }
+ Disconnected?.Invoke();
+
+ }
+
+ /// <summary>
+ /// Occurs when you are disconnected from the Digital Society.
+ /// </summary>
+ public static event EmptyEventHandler Disconnected;
+
+ /// <summary>
+ /// Occurs when the unique ID for this client is sent by the server.
+ /// </summary>
+ public static event Action<string> GUIDReceived;
+
+ private static void ServerManager_MessageReceived(ServerMessage msg)
+ {
+ switch(msg.Name)
+ {
+ case "getguid_fromserver":
+ if(SaveSystem.CurrentSave.Username == msg.Contents)
+ {
+ client.Send(new NetObject("yes_i_am", new ServerMessage
+ {
+ Name = "getguid_reply",
+ GUID = msg.GUID,
+ Contents = thisGuid.ToString(),
+ }));
+ }
+ break;
+ case "getguid_reply":
+ GUIDReceived?.Invoke(msg.Contents);
+ break;
+ }
+ }
+
+ public static void Detach_ServerManager_MessageReceived()
+ {
+ MessageReceived -= new ServerMessageReceived(ServerManager_MessageReceived);
+ }
+
+ /// <summary>
+ /// Initiate a new Digital Society connection.
+ /// </summary>
+ /// <param name="mud_address">The IP address or hostname of the target server</param>
+ /// <param name="port">The target port.</param>
+ public static void Initiate(string mud_address, int port)
+ {
+ client = new NetObjectClient();
+ client.OnDisconnected += (o, a) =>
+ {
+ if (!UserDisconnect)
+ {
+ Desktop.PushNotification("digital_society_connection", "Disconnected from Digital Society.", "The ShiftOS kernel has been disconnected from the Digital Society. We are attempting to re-connect you.");
+ TerminalBackend.PrefixEnabled = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.Red;
+ ConsoleEx.Bold = true;
+ Console.Write($@"Disconnected from MUD: ");
+ ConsoleEx.Bold = false;
+ ConsoleEx.Italic = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine("You have been disconnected from the multi-user domain for an unknown reason. Your save data is preserved within the kernel and you will be reconnected shortly.");
+ TerminalBackend.PrefixEnabled = true;
+ TerminalBackend.PrintPrompt();
+ Initiate(mud_address, port);
+ }
+ };
+ client.OnReceived += (o, a) =>
+ {
+ if (PingTimer.IsRunning)
+ {
+ DigitalSocietyPing = PingTimer.ElapsedMilliseconds;
+ PingTimer.Reset();
+ }
+ var msg = a.Data.Object as ServerMessage;
+ if (msg.Name == "Welcome")
+ {
+ thisGuid = new Guid(msg.Contents);
+ GUIDReceived?.Invoke(msg.Contents);
+ TerminalBackend.PrefixEnabled = true;
+ TerminalBackend.PrintPrompt();
+ }
+ else if(msg.Name == "allusers")
+ {
+ foreach(var acc in JsonConvert.DeserializeObject<string[]>(msg.Contents))
+ {
+ Console.WriteLine(acc);
+ }
+ TerminalBackend.PrintPrompt();
+ }
+ else if(msg.Name == "update_your_cp")
+ {
+ var args = JsonConvert.DeserializeObject<Dictionary<string, object>>(msg.Contents);
+ if(args["username"] as string == SaveSystem.CurrentSave.Username)
+ {
+ SaveSystem.CurrentSave.Codepoints += (long)args["amount"];
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ Infobox.Show($"MUD Control Centre", $"Someone bought an item in your shop, and they have paid {args["amount"]}, and as such, you have been granted these Codepoints.");
+ }));
+ SaveSystem.SaveGame();
+ }
+ }
+ else if(msg.Name =="broadcast")
+ {
+ Console.WriteLine(msg.Contents);
+ }
+ else if(msg.Name == "forward")
+ {
+ MessageReceived?.Invoke(JsonConvert.DeserializeObject<ServerMessage>(msg.Contents));
+ }
+ else if (msg.Name == "Error")
+ {
+ var ex = JsonConvert.DeserializeObject<Exception>(msg.Contents);
+ TerminalBackend.PrefixEnabled = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.Red;
+ ConsoleEx.Bold = true;
+ Console.Write($@"{{MUD_ERROR}}: ");
+ ConsoleEx.Bold = false;
+ ConsoleEx.Italic = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine(ex.Message);
+ TerminalBackend.PrefixEnabled = true;
+ TerminalBackend.PrintPrompt();
+ }
+ else
+ {
+ MessageReceived?.Invoke(msg);
+ }
+ };
+
+ try
+ {
+ client.Connect(mud_address, port);
+ }
+ catch(SocketException ex)
+ {
+ System.Diagnostics.Debug.Print(ex.ToString());
+ Initiate(mud_address, port);
+ }
+ }
+
+ private static Stopwatch PingTimer = new Stopwatch();
+
+ /// <summary>
+ /// Send a message to the server.
+ /// </summary>
+ /// <param name="name">The message name</param>
+ /// <param name="contents">The message body</param>
+ public static void SendMessage(string name, string contents)
+ {
+ var sMsg = new ServerMessage
+ {
+ Name = name,
+ Contents = contents,
+ GUID = thisGuid.ToString(),
+ };
+ PingTimer.Start();
+ client.Send(new NetObject("msg", sMsg));
+
+ }
+
+ private static bool singleplayer = false;
+ public static bool IsSingleplayer { get { return singleplayer; } }
+
+ public static void StartLANServer()
+ {
+ singleplayer = true;
+ ShiftOS.Server.Program.ServerStarted += (address) =>
+ {
+ Console.WriteLine($"Connecting to {address}...");
+ Initiate(address, 13370);
+ };
+ Disconnected += () =>
+ {
+ ShiftOS.Server.Program.Stop();
+ };
+ ShiftOS.Server.Program.Main(new[] { "" });
+
+
+ }
+
+ /// <summary>
+ /// Occurs when the server sends a message to this client.
+ /// </summary>
+ public static event ServerMessageReceived MessageReceived;
+
+ /// <summary>
+ /// Send a message to another client.
+ /// </summary>
+ /// <param name="targetGUID">The target client GUID.</param>
+ /// <param name="title">The message title</param>
+ /// <param name="message">The message contents</param>
+ public static void Forward(string targetGUID, string title, string message)
+ {
+ var smsg = new ServerMessage
+ {
+ GUID = targetGUID,
+ Name = title,
+ Contents = message
+ };
+ ServerManager.SendMessage("mud_forward", JsonConvert.SerializeObject(smsg));
+ }
+ }
+
+ /// <summary>
+ /// Delegate for handling server messages
+ /// </summary>
+ /// <param name="msg">A server message containing the protocol message name, GUID of the sender, and the contents of the message.</param>
+ public delegate void ServerMessageReceived(ServerMessage msg);
+
+ public class MultiplayerOnlyAttribute : Attribute
+ {
+ /// <summary>
+ /// Marks this application as a multiplayer-only application.
+ /// </summary>
+ public MultiplayerOnlyAttribute()
+ {
+
+ }
+ }
+}
diff --git a/ShiftOS_TheReturn/ShiftOS.Engine.csproj b/ShiftOS_TheReturn/ShiftOS.Engine.csproj
index 3b5eadd..f0993a8 100644
--- a/ShiftOS_TheReturn/ShiftOS.Engine.csproj
+++ b/ShiftOS_TheReturn/ShiftOS.Engine.csproj
@@ -133,9 +133,7 @@
<Compile Include="TerminalTextWriter.cs" />
<Compile Include="TutorialManager.cs" />
<Compile Include="UniteClient.cs" />
- <Compile Include="UniteTestCommands.cs" />
<Compile Include="UserManagementCommands.cs" />
- <Compile Include="VirusEngine.cs" />
<Compile Include="WinOpenAttribute.cs" />
<EmbeddedResource Include="Infobox.resx">
<DependentUpon>Infobox.cs</DependentUpon>
diff --git a/ShiftOS_TheReturn/ShiftnetSite.cs b/ShiftOS_TheReturn/ShiftnetSite.cs
index 5460171..07b4698 100644
--- a/ShiftOS_TheReturn/ShiftnetSite.cs
+++ b/ShiftOS_TheReturn/ShiftnetSite.cs
@@ -6,13 +6,34 @@ using System.Threading.Tasks;
namespace ShiftOS.Engine
{
+ /// <summary>
+ /// Interface for creating a Shiftnet website.
+ /// </summary>
public interface IShiftnetSite
{
+ /// <summary>
+ /// Called when the page is loaded. Perform data population here.
+ /// </summary>
void Setup();
+
+ /// <summary>
+ /// Occurs when a ShiftOS skin is loaded.
+ /// </summary>
void OnSkinLoad();
+
+ /// <summary>
+ /// Occurs when a Shiftorium upgrade is installed.
+ /// </summary>
void OnUpgrade();
+ /// <summary>
+ /// Invoke this to navigate the parent browser to a specified Shiftnet URL.
+ /// </summary>
event Action<string> GoToUrl;
+
+ /// <summary>
+ /// Invoke this to tell the parent browser to navigate to the previous page.
+ /// </summary>
event Action GoBack;
}
@@ -24,14 +45,35 @@ namespace ShiftOS.Engine
}
+ /// <summary>
+ /// Interface for creating a Shiftnet client.
+ /// </summary>
public interface IShiftnetClient
{
+ /// <summary>
+ /// Navigates the client to a specified Shiftnet URL.
+ /// </summary>
+ /// <param name="url">The URL to navigate to.</param>
void NavigateToUrl(string url);
+
+ /// <summary>
+ /// Refreshes the current page.
+ /// </summary>
void RefreshSite();
}
+ /// <summary>
+ /// Marks this class as a Shiftnet website.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple =false)]
public class ShiftnetSiteAttribute : Attribute
{
+ /// <summary>
+ /// Creates a new instance of the <see cref="ShiftnetSiteAttribute"/> class.
+ /// </summary>
+ /// <param name="url">The URL that links to this site</param>
+ /// <param name="name">The name of this site</param>
+ /// <param name="description">The description of this site</param>
public ShiftnetSiteAttribute(string url, string name, string description)
{
Url = url;
@@ -39,8 +81,19 @@ namespace ShiftOS.Engine
Description = description;
}
+ /// <summary>
+ /// Gets the Shiftnet URL for this site.
+ /// </summary>
public string Url { get; private set; }
+
+ /// <summary>
+ /// Gets the name of this website.
+ /// </summary>
public string Name { get; private set; }
+
+ /// <summary>
+ /// Gets the description of this website.
+ /// </summary>
public string Description { get; private set; }
}
}
diff --git a/ShiftOS_TheReturn/Shiftorium.cs b/ShiftOS_TheReturn/Shiftorium.cs
new file mode 100644
index 0000000..fe436d4
--- /dev/null
+++ b/ShiftOS_TheReturn/Shiftorium.cs
@@ -0,0 +1,563 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using static ShiftOS.Engine.SaveSystem;
+using System.Diagnostics;
+
+namespace ShiftOS.Engine
+{
+ /// <summary>
+ /// Backend class for the Shiftorium.
+ /// </summary>
+ public static class Shiftorium
+ {
+ /// <summary>
+ /// Whether or not shiftorium output should be written to the console.
+ /// </summary>
+ public static bool Silent = false;
+
+ /// <summary>
+ /// Gets all Shiftorium categories.
+ /// </summary>
+ /// <param name="onlyAvailable">Should we look in the "available" upgrade list (i.e, what the user can buy right now), or the full upgrade list?</param>
+ /// <returns>All Shiftorium categories from the list, in a <see cref="System.String[]"/>. </returns>
+ public static string[] GetCategories(bool onlyAvailable = true)
+ {
+ List<string> cats = new List<string>();
+ IEnumerable < ShiftoriumUpgrade > upgrades = GetDefaults();
+ if (onlyAvailable)
+ upgrades = new List<ShiftoriumUpgrade>(GetAvailable());
+
+ foreach(var upg in upgrades)
+ {
+ if (!cats.Contains(upg.Category))
+ cats.Add(upg.Category);
+ }
+
+ return cats.ToArray();
+ }
+
+ /// <summary>
+ /// Causes the engine to alert the frontend of a new Shiftorium upgrade install.
+ /// </summary>
+ public static void InvokeUpgradeInstalled()
+ {
+ Installed?.Invoke();
+ }
+
+ /// <summary>
+ /// Gets the category of an upgrade.
+ /// </summary>
+ /// <param name="id">The upgrade ID to check</param>
+ /// <returns>"Other" if the upgrade is not found, else, the upgrade category.</returns>
+ public static string GetCategory(string id)
+ {
+ var upg = GetDefaults().FirstOrDefault(x => x.ID == id);
+ if (upg == null)
+ return "Other";
+ return (upg.Category == null) ? "Other" : upg.Category;
+ }
+
+ /// <summary>
+ /// Gets all upgrades in a given category.
+ /// </summary>
+ /// <param name="cat">The category name to search</param>
+ /// <returns>The upgrades in the category.</returns>
+ public static IEnumerable<ShiftoriumUpgrade> GetAllInCategory(string cat)
+ {
+ return GetDefaults().Where(x => x.Category == cat);
+ }
+
+ /// <summary>
+ /// Gets whether or not the user has installed all upgrades in a category.
+ /// </summary>
+ /// <param name="cat">The category to search.</param>
+ /// <returns>Boolean value representing whether the user has installed all upgrades in the category.</returns>
+ public static bool IsCategoryEmptied(string cat)
+ {
+ return GetDefaults().Where(x => x.Category == cat).FirstOrDefault(x => x.Installed == false) == null;
+ }
+
+ /// <summary>
+ /// Buy an upgrade, deducting the specified amount of Codepoints.
+ /// </summary>
+ /// <param name="id">The upgrade ID to buy</param>
+ /// <param name="cost">The amount of Codepoints to deduct</param>
+ /// <returns>True if the upgrade was installed successfully, false if the user didn't have enough Codepoints or the upgrade wasn' found.</returns>
+ public static bool Buy(string id, long cost)
+ {
+ if(SaveSystem.CurrentSave.Codepoints >= cost)
+ {
+ SaveSystem.CurrentSave.Upgrades[id] = true;
+ TerminalBackend.InvokeCommand("sos.save");
+ SaveSystem.TransferCodepointsToVoid(cost);
+ Installed?.Invoke();
+ Desktop.ResetPanelButtons();
+ Desktop.PopulateAppLauncher();
+ return true;
+ }
+ else
+ {
+ if(!Silent)
+ Console.WriteLine($"{{SHIFTORIUM_NOTENOUGHCP}}: {cost} > {SaveSystem.CurrentSave.Codepoints}");
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Determines whether all Shiftorium upgrade attributes for this type have been installed.
+ /// </summary>
+ /// <param name="type">The type to scan</param>
+ /// <returns>Boolean value representing the result of this function.</returns>
+ public static bool UpgradeAttributesUnlocked(Type type)
+ {
+ foreach(var attr in type.GetCustomAttributes(true))
+ {
+ if(attr is RequiresUpgradeAttribute)
+ {
+ var rAttr = attr as RequiresUpgradeAttribute;
+ return rAttr.Installed;
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Determines whether all Shiftorium upgrade attributes for this method have been installed.
+ /// </summary>
+ /// <param name="type">The method to scan</param>
+ /// <returns>Boolean value representing the result of this function.</returns>
+ public static bool UpgradeAttributesUnlocked(MethodInfo type)
+ {
+ foreach (var attr in type.GetCustomAttributes(true))
+ {
+ if (attr is RequiresUpgradeAttribute)
+ {
+ var rAttr = attr as RequiresUpgradeAttribute;
+ return rAttr.Installed;
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Determines whether all Shiftorium upgrade attributes for this property have been installed.
+ /// </summary>
+ /// <param name="type">The property to scan</param>
+ /// <returns>Boolean value representing the result of this function.</returns>
+ public static bool UpgradeAttributesUnlocked(PropertyInfo type)
+ {
+ foreach (var attr in type.GetCustomAttributes(true))
+ {
+ if (attr is RequiresUpgradeAttribute)
+ {
+ var rAttr = attr as RequiresUpgradeAttribute;
+ return rAttr.Installed;
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Determines whether all Shiftorium upgrade attributes for this field have been installed.
+ /// </summary>
+ /// <param name="type">The field to scan</param>
+ /// <returns>Boolean value representing the result of this function.</returns>
+ public static bool UpgradeAttributesUnlocked(FieldInfo type)
+ {
+ foreach (var attr in type.GetCustomAttributes(true))
+ {
+ if (attr is RequiresUpgradeAttribute)
+ {
+ var rAttr = attr as RequiresUpgradeAttribute;
+ return rAttr.Installed;
+ }
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Gets or sets whether the Shiftorium has been initiated.
+ /// </summary>
+ public static bool IsInitiated { get; private set; }
+
+
+ /// <summary>
+ /// Initiates the Shiftorium.
+ /// </summary>
+ public static void Init()
+ {
+ if (IsInitiated == false)
+ {
+ IsInitiated = true;
+ //Let the crash handler deal with this one...
+ var dict = GetDefaults();
+ foreach (var itm in dict)
+ {
+ if (!SaveSystem.CurrentSave.Upgrades.ContainsKey(itm.ID))
+ {
+ try
+ {
+ SaveSystem.CurrentSave.Upgrades.Add(itm.ID, false);
+ }
+ catch
+ {
+
+ }
+ }
+ }
+ }
+
+ }
+
+ /// <summary>
+ /// Get the codepoint value for an upgrade.
+ /// </summary>
+ /// <param name="id">The upgrade ID to search</param>
+ /// <returns>The codepoint value.</returns>
+ public static long GetCPValue(string id)
+ {
+ foreach(var upg in GetDefaults())
+ {
+ if (upg.ID == id)
+ return upg.Cost;
+ }
+ return 0;
+ }
+
+ /// <summary>
+ /// Gets all available upgrades.
+ /// </summary>
+ /// <returns></returns>
+ public static ShiftoriumUpgrade[] GetAvailable()
+ {
+ List<ShiftoriumUpgrade> available = new List<ShiftoriumUpgrade>();
+ foreach(var defaultupg in GetDefaults())
+ {
+ if (!UpgradeInstalled(defaultupg.ID) && DependenciesInstalled(defaultupg))
+ available.Add(defaultupg);
+ }
+ return available.ToArray();
+ }
+
+ /// <summary>
+ /// Determines whether all dependencies of a given upgrade have been installed.
+ /// </summary>
+ /// <param name="upg">The upgrade to scan</param>
+ /// <returns>Boolean representing the result of this function.</returns>
+ public static bool DependenciesInstalled(ShiftoriumUpgrade upg)
+ {
+ if (string.IsNullOrEmpty(upg.Dependencies))
+ {
+ return true;//root upgrade, no parents
+ }
+ else if (upg.Dependencies.Contains(";"))
+ {
+ string[] dependencies = upg.Dependencies.Split(';');
+ foreach(var dependency in dependencies)
+ {
+ if (!UpgradeInstalled(dependency))
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ return UpgradeInstalled(upg.Dependencies);
+ }
+ }
+
+ /// <summary>
+ /// Fired when an upgrade is installed.
+ /// </summary>
+ public static event EmptyEventHandler Installed;
+
+ /// <summary>
+ /// Determines if an upgrade is installed.
+ /// </summary>
+ /// <param name="id">The upgrade ID to scan.</param>
+ /// <returns>Whether the upgrade is installed.</returns>
+ public static bool UpgradeInstalled(string id)
+ {
+ if (SaveSystem.CurrentSave != null)
+ {
+ if (!IsInitiated)
+ Init();
+ }
+ try
+ {
+ if (SaveSystem.CurrentSave == null)
+ return false;
+
+ if (SaveSystem.CurrentSave.StoriesExperienced == null)
+ SaveSystem.CurrentSave.StoriesExperienced = new List<string>();
+
+ if (id.Contains(';'))
+ {
+ foreach(var u in id.Split(';'))
+ {
+ if (UpgradeInstalled(u) == false)
+ return false;
+ }
+ return true;
+ }
+
+ bool upgInstalled = false;
+ if(SaveSystem.CurrentSave.Upgrades.ContainsKey(id))
+ upgInstalled = SaveSystem.CurrentSave.Upgrades[id];
+
+ if(upgInstalled == false)
+ return SaveSystem.CurrentSave.StoriesExperienced.Contains(id);
+ return true;
+ }
+ catch
+ {
+ Console.WriteLine("Upgrade " + id + "DNE.");
+ Console.WriteLine();
+ return false;
+ }
+
+ }
+
+ //LEAVE THIS AS FALSE. The game will set it when the save is loaded.
+ public static bool LogOrphanedUpgrades = false;
+
+ private static IShiftoriumProvider _provider = null;
+
+ [Obsolete("Please annotate your provider with a [ShiftoriumProvider] attribute instead. This function doesn't do anything.")]
+ public static void RegisterProvider(IShiftoriumProvider p)
+ {
+ _provider = p;
+ }
+
+ /// <summary>
+ /// Gets every upgrade inside the frontend and all mods.
+ /// </summary>
+ /// <returns>Every single found Shiftorium upgrade.</returns>
+ public static List<ShiftoriumUpgrade> GetDefaults()
+ {
+ List<ShiftoriumUpgrade> list = new List<ShiftoriumUpgrade>();
+ //Now we probe for ShiftoriumUpgradeAttributes for mods.
+ foreach(var file in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ if(file.EndsWith(".exe") || file.EndsWith(".dll"))
+ {
+ try
+ {
+ var asm = Assembly.LoadFile(file);
+ foreach (var type in asm.GetTypes())
+ {
+ if (type.GetInterfaces().Contains(typeof(IShiftoriumProvider)))
+ {
+ if(type.GetCustomAttributes().FirstOrDefault(x=> x is ShiftoriumProviderAttribute) != null)
+ {
+ var _p = Activator.CreateInstance(type, null) as IShiftoriumProvider;
+ list.AddRange(_p.GetDefaults());
+ }
+ }
+
+
+ ShiftoriumUpgradeAttribute attrib = type.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute;
+ if (attrib != null)
+ {
+ if (list.FirstOrDefault(x => x.ID == attrib.Upgrade) != null)
+ throw new ShiftoriumConflictException(attrib.Upgrade);
+ list.Add(new ShiftoriumUpgrade
+ {
+ Id = attrib.Upgrade,
+ Name = attrib.Name,
+ Cost = attrib.Cost,
+ Description = attrib.Description,
+ Dependencies = attrib.Dependencies,
+ Category = attrib.Category
+ });
+ }
+
+ foreach (var mth in type.GetMethods())
+ {
+ attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute;
+ if (attrib != null)
+ {
+ if (list.FirstOrDefault(x => x.ID == attrib.Upgrade) != null)
+ throw new ShiftoriumConflictException(attrib.Upgrade);
+ list.Add(new ShiftoriumUpgrade
+ {
+ Id = attrib.Upgrade,
+ Name = attrib.Name,
+ Cost = attrib.Cost,
+ Description = attrib.Description,
+ Dependencies = attrib.Dependencies,
+ Category = attrib.Category
+ });
+
+ }
+ }
+
+ foreach (var mth in type.GetFields())
+ {
+ attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute;
+ if (attrib != null)
+ {
+ if (list.FirstOrDefault(x => x.ID == attrib.Upgrade) != null)
+ throw new ShiftoriumConflictException(attrib.Upgrade);
+ list.Add(new ShiftoriumUpgrade
+ {
+ Id = attrib.Upgrade,
+ Name = attrib.Name,
+ Cost = attrib.Cost,
+ Description = attrib.Description,
+ Dependencies = attrib.Dependencies,
+ Category = attrib.Category
+ });
+
+ }
+ }
+
+ foreach (var mth in type.GetProperties())
+ {
+ attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute;
+ if (attrib != null)
+ {
+ if (list.FirstOrDefault(x => x.ID == attrib.Upgrade) != null)
+ throw new ShiftoriumConflictException(attrib.Upgrade);
+ list.Add(new ShiftoriumUpgrade
+ {
+ Id = attrib.Upgrade,
+ Name = attrib.Name,
+ Cost = attrib.Cost,
+ Description = attrib.Description,
+ Dependencies = attrib.Dependencies,
+ Category = attrib.Category
+ });
+
+ }
+ }
+
+ }
+ }
+ catch { }
+ }
+ }
+
+
+
+ foreach(var item in list)
+ {
+ if (list.Where(x => x.ID == item.ID).Count() > 1)
+ throw new ShiftoriumConflictException(item.Id);
+ }
+ return list;
+ }
+ }
+
+ public interface IShiftoriumProvider
+ {
+ /// <summary>
+ /// Retrieves all frontend upgrades.
+ /// </summary>
+ /// <returns></returns>
+ List<ShiftoriumUpgrade> GetDefaults();
+ }
+
+ public class ShiftoriumUpgradeLookupException : Exception
+ {
+ public ShiftoriumUpgradeLookupException(string id) : base("A shiftorium upgrade of ID \"" + id + "\" was not found in the system.")
+ {
+ ID = id;
+
+ Debug.WriteLine("UpgradeNotFound: " + id);
+
+ }
+
+ public string ID { get; private set; }
+ }
+
+
+
+ public class ShiftoriumUpgrade
+ {
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public long Cost { get; set; }
+ public string ID { get { return (this.Id != null ? this.Id : (Name.ToLower().Replace(" ", "_"))); } }
+ public string Id { get; set; }
+ public string Category { get; set; }
+ public bool Installed
+ {
+ get
+ {
+ return Shiftorium.UpgradeInstalled(ID);
+ }
+ }
+ public string Dependencies { get; set; }
+ }
+
+ public class ShiftoriumUpgradeAttribute : RequiresUpgradeAttribute
+ {
+ public ShiftoriumUpgradeAttribute(string name, long cost, string desc, string dependencies, string category) : base(name.ToLower().Replace(" ", "_"))
+ {
+ Name = name;
+ Description = desc;
+ Dependencies = dependencies;
+ Cost = cost;
+ Category = category;
+ }
+
+ public string Name { get; private set; }
+ public string Description { get; private set; }
+ public long Cost { get; private set; }
+ public string Dependencies { get; private set; }
+ public string Category { get; private set; }
+ }
+
+ public class ShiftoriumConflictException : Exception
+ {
+ public ShiftoriumConflictException() : base("An upgrade conflict has occurred while loading Shiftorium Upgrades from an assembly. Is there a duplicate upgrade ID?")
+ {
+
+ }
+
+ public ShiftoriumConflictException(string id) : base("An upgrade conflict has occurred while loading Shiftorium Upgrades from an assembly. An upgrade with the ID \"" + id + "\" has already been loaded.")
+ {
+
+ }
+ }
+
+ public class ShiftoriumProviderAttribute : Attribute
+ {
+
+ }
+}
diff --git a/ShiftOS_TheReturn/Skinning.cs b/ShiftOS_TheReturn/Skinning.cs
new file mode 100644
index 0000000..f5dd211
--- /dev/null
+++ b/ShiftOS_TheReturn/Skinning.cs
@@ -0,0 +1,1570 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using System.Windows.Forms;
+using static ShiftOS.Engine.SaveSystem;
+using ShiftOS.Objects.ShiftFS;
+using System.Reflection;
+using ShiftOS.Engine.Scripting;
+namespace ShiftOS.Engine
+{
+ /// <summary>
+ /// Skinning API for Lua.
+ /// </summary>
+ [Exposed("skinning")]
+ public class SkinFunctions
+ {
+ /// <summary>
+ /// Reload the current skin.
+ /// </summary>
+ public void loadSkin()
+ {
+ SkinEngine.LoadSkin();
+ }
+
+ /// <summary>
+ /// Get the current skin info.
+ /// </summary>
+ /// <returns>A proxy object containing all skin variables.</returns>
+ public dynamic getSkin()
+ {
+ return SkinEngine.LoadedSkin;
+ }
+
+ /// <summary>
+ /// Set the current skin to the specified <see cref="Skin"/> class.
+ /// </summary>
+ /// <param name="skn">The <see cref="Skin"/> class to load.</param>
+ public void setSkin(Skin skn)
+ {
+ Utils.WriteAllText(Paths.GetPath("skin.json"), JsonConvert.SerializeObject(skn));
+ SkinEngine.LoadSkin();
+ }
+
+ /// <summary>
+ /// Retrieves an image from the skin file.
+ /// </summary>
+ /// <param name="id">The skin image ID</param>
+ /// <returns>The loaded image, null (nil in Lua) if none is found.</returns>
+ public dynamic getImage(string id)
+ {
+ return SkinEngine.GetImage(id);
+ }
+ }
+
+ /// <summary>
+ /// Skin engine management class.
+ /// </summary>
+ public static class SkinEngine
+ {
+ private static ISkinPostProcessor processor = null;
+
+ /// <summary>
+ /// Load a new skin postprocessor into the engine.
+ /// </summary>
+ /// <param name="_processor">The postprocessor to load.</param>
+ public static void SetPostProcessor(ISkinPostProcessor _processor)
+ {
+ processor = _processor;
+ }
+
+ /// <summary>
+ /// Retrieve the user-specified image layout of a skin image.
+ /// </summary>
+ /// <param name="img">The skin image ID.</param>
+ /// <returns>The <see cref="ImageLayout"/> for the image.</returns>
+ public static ImageLayout GetImageLayout(string img)
+ {
+ if (LoadedSkin.SkinImageLayouts.ContainsKey(img))
+ {
+ return LoadedSkin.SkinImageLayouts[img];
+ }
+ else
+ {
+ LoadedSkin.SkinImageLayouts.Add(img, ImageLayout.Tile);
+ return ImageLayout.Tile;
+ }
+ }
+
+ /// <summary>
+ /// Retrieves an image from the skin after postprocessing it.
+ /// </summary>
+ /// <param name="img">The image ID to search.</param>
+ /// <returns>The post-processed image, or null if none was found.</returns>
+ public static System.Drawing.Image GetImage(string img)
+ {
+ var type = typeof(Skin);
+
+ foreach (var field in type.GetFields())
+ {
+ foreach (var attr in field.GetCustomAttributes(false))
+ {
+ if (attr is ImageAttribute)
+ {
+ var iattr = attr as ImageAttribute;
+ if (iattr.Name == img)
+ {
+ byte[] image = (byte[])field.GetValue(LoadedSkin);
+ if (processor != null)
+ image = processor.ProcessImage(image);
+ return ImageFromBinary(image);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Set the engine's current icon prober.
+ /// </summary>
+ /// <param name="prober">The icon prober to use.</param>
+ public static void SetIconProber(IIconProber prober)
+ {
+ _iconProber = prober;
+ }
+
+ /// <summary>
+ /// Load a <see cref="Image"/> from a <see cref="byte"/> array.
+ /// </summary>
+ /// <param name="image">The array to convert</param>
+ /// <returns>The resulting image.</returns>
+ public static Image ImageFromBinary(byte[] image)
+ {
+ if (image == null)
+ return null;
+ Image img = (Bitmap)((new ImageConverter()).ConvertFrom(image));
+ return img;
+ }
+
+ private static Skin loadedSkin = new Skin();
+
+ /// <summary>
+ /// Gets the currently loaded skin.
+ /// </summary>
+ public static Skin LoadedSkin
+ {
+ get
+ {
+ return loadedSkin;
+ }
+ private set
+ {
+ loadedSkin = value;
+ }
+ }
+
+ /// <summary>
+ /// Initiates the skin engine.
+ /// </summary>
+ public static void Init()
+ {
+ Application.ApplicationExit += (o, a) =>
+ {
+ SaveSkin();
+ };
+
+ if (!Utils.FileExists(Paths.GetPath("skin.json")))
+ {
+ LoadedSkin = new ShiftOS.Engine.Skin();
+ SaveSkin();
+ }
+ else
+ {
+ LoadSkin();
+ }
+ if (SaveSystem.CurrentSave != null)
+ {
+ SkinLoaded?.Invoke();
+ }
+ }
+
+ /// <summary>
+ /// Occurs when the skin is loaded.
+ /// </summary>
+ public static event EmptyEventHandler SkinLoaded;
+
+ /// <summary>
+ /// Reload the current skin.
+ /// </summary>
+ public static void LoadSkin()
+ {
+ LoadedSkin = JsonConvert.DeserializeObject<Skin>(Utils.ReadAllText(Paths.GetPath("skin.json")));
+ SkinLoaded?.Invoke();
+ Desktop.ResetPanelButtons();
+ Desktop.PopulateAppLauncher();
+ }
+
+ /// <summary>
+ /// Save the skin loaded in memory to the filesystem.
+ /// </summary>
+ public static void SaveSkin()
+ {
+ Utils.WriteAllText(Paths.GetPath("skin.json"), JsonConvert.SerializeObject(LoadedSkin, Formatting.Indented));
+ }
+
+ private static IIconProber _iconProber = null;
+
+ /// <summary>
+ /// Retrieves the default icon for a given icon ID.
+ /// </summary>
+ /// <param name="id">The icon ID to search.</param>
+ /// <returns>The resulting icon image.</returns>
+ public static Image GetDefaultIcon(string id)
+ {
+ if (_iconProber == null)
+ {
+ return new Bitmap(16, 16);
+ }
+ else
+ {
+ foreach (var f in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ if (f.EndsWith(".exe") || f.EndsWith(".dll"))
+ {
+ try
+ {
+ var asm = Assembly.LoadFile(f);
+ foreach (var type in asm.GetTypes())
+ {
+ if (type.Name == id)
+ {
+ foreach (var attr in type.GetCustomAttributes(true))
+ {
+ if (attr is DefaultIconAttribute)
+ {
+ return _iconProber.GetIcon(attr as DefaultIconAttribute);
+ }
+ }
+ }
+ }
+ }
+ catch { }
+ }
+ }
+ return new Bitmap(16, 16);
+ }
+ }
+
+ /// <summary>
+ /// Retrieves the user-defined icon for a specified icon ID.
+ /// </summary>
+ /// <param name="id">The icon ID to search.</param>
+ /// <returns>The resulting icon image.</returns>
+ public static Image GetIcon(string id)
+ {
+ if (!LoadedSkin.AppIcons.ContainsKey(id))
+ LoadedSkin.AppIcons.Add(id, null);
+
+ if (LoadedSkin.AppIcons[id] == null)
+ return GetDefaultIcon(id);
+ else
+ {
+ using (var sr = new MemoryStream(LoadedSkin.AppIcons[id]))
+ {
+ return Image.FromStream(sr);
+ }
+ }
+
+ }
+ }
+
+ /// <summary>
+ /// Interface for probing app icons.
+ /// </summary>
+ public interface IIconProber
+ {
+ /// <summary>
+ /// Retrieve the icon image from a <see cref="DefaultIconAttribute"/>.
+ /// </summary>
+ /// <param name="attr">The attribute data</param>
+ /// <returns>The resulting image.</returns>
+ Image GetIcon(DefaultIconAttribute attr);
+ }
+
+ /// <summary>
+ /// Sets the default icon ID for a <see cref="IShiftOSWindow"/>.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple =false)]
+ public class DefaultIconAttribute : Attribute
+ {
+ public DefaultIconAttribute(string id)
+ {
+ ID = id;
+ }
+
+ public string ID { get; private set; }
+ }
+
+ /// <summary>
+ /// The data stored in any .skn file.
+ /// </summary>
+ public class Skin
+ {
+ //borrowing from the discourse theme for the default skin
+ private static readonly Color DefaultBackground = Color.FromArgb(0, 0x44, 0x00);
+ private static readonly Color DefaultForeground = Color.FromArgb(0xDD, 0xDD, 0xDD);
+ private static readonly Color Accent1 = Color.FromArgb(0x66, 0x66, 0x66);
+ private static readonly Color Accent2 = Color.FromArgb(0x80, 0, 0);
+ private static readonly Color DesktopBG = Color.FromArgb(0x22, 0x22, 0x22);
+ private static readonly Font SysFont = new Font("Tahoma", 9F);
+ private static readonly Font SysFont2 = new Font("Tahoma", 10F, FontStyle.Bold);
+ private static readonly Font Header1 = new Font("Helvetica", 20F, FontStyle.Bold);
+ private static readonly Font Header2 = new Font("Helvetica", 17.5F, FontStyle.Bold);
+ private static readonly Font Header3 = new Font("Tahoma", 15F, FontStyle.Bold);
+
+ private static readonly Color TitleBG = Color.FromArgb(0x11, 0x11, 0x11);
+ private static readonly Color TitleFG = Color.FromArgb(0xaa, 0xaa, 0xaa);
+
+ //Todo: When making Shifter GUI we need to label all these with proper Shifter attributes to get 'em displaying in the right places.
+
+ public override string ToString()
+ {
+ return JsonConvert.SerializeObject(this, Formatting.Indented);
+ }
+
+ [ShifterHidden]
+ public Dictionary<string, string> AppNames = new Dictionary<string, string>();
+
+ [ShifterHidden]
+ public Dictionary<string, byte[]> AppIcons = new Dictionary<string, byte[]>();
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Progress Bar")]
+ [RequiresUpgrade("shift_progress_bar;skinning")]
+ [Image("progressbarbg")]
+ [ShifterName("Progress Bar Background Image")]
+ [ShifterDescription("Set an image for the background of a progress bar.")]
+ public byte[] ProgressBarBG = null;
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Progress Bar")]
+ [RequiresUpgrade("shift_progress_bar;skinning")]
+ [Image("progress")]
+ [ShifterName("Progress Image")]
+ [ShifterDescription("Set the image for the progress inside a progress bar.")]
+ public byte[] Progress = null;
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Progress Bar")]
+ [RequiresUpgrade("shift_progress_bar")]
+ [ShifterName("Progress bar foreground color")]
+ [ShifterDescription("Set the color of the progress indicator.")]
+ public Color ProgressColor = Accent1;
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Progress Bar")]
+ [RequiresUpgrade("shift_progress_bar")]
+ [ShifterName("Progress bar background color")]
+ [ShifterDescription("The background color of the progress bar.")]
+ public Color ProgressBarBackgroundColor = Color.Black;
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Progress Bar")]
+ [RequiresUpgrade("shift_progress_bar")]
+ [ShifterName("Progress bar block size")]
+ [ShifterDescription("If the progress bar style is set to Blocks, this determines how wide each block should be.")]
+ public int ProgressBarBlockSize = 15;
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Progress Bar")]
+ [RequiresUpgrade("shift_progress_bar")]
+ [ShifterDescription("Set the style of a progress bar.\r\nMarquee: The progress bar will render a box that moves from the left to the right in a loop.\r\nContinuous: Progress is shown by a single, continuous box.\r\nBlocks: Just like Continuous, but the box is split into even smaller boxes of a set width.")]
+ [ShifterName("Progress bar style")]
+ public ProgressBarStyle ProgressBarStyle = ProgressBarStyle.Continuous;
+
+
+
+
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("shift_buttons")]
+ [ShifterName("Button background color")]
+ [ShifterDescription("Set the background color for each button's Idle state.")]
+ public Color ButtonBackgroundColor = Skin.DefaultBackground;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("shift_buttons;skinning")]
+ [Image("buttonhover")]
+ [ShifterName("Button hover image")]
+ [ShifterDescription("Set the image that's displayed when the mouse hovers over a button.")]
+ public byte[] ButtonHoverImage = null;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("skinning;shift_buttons")]
+ [Image("buttonpressed")]
+ [ShifterName("Button pressed image")]
+ [ShifterDescription("Select an image to show when the user presses a button.")]
+ public byte[] ButtonPressedImage = null;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("shift_buttons")]
+ [ShifterName("Button hover color")]
+ [ShifterDescription("Choose the color that displays on a button when the mouse hovers over it.")]
+ public Color ButtonHoverColor = Skin.Accent1;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("shift_buttons")]
+ [ShifterName("Button pressed color")]
+ [ShifterDescription("Select the background color for the button when the mouse clicks it.")]
+ public Color ButtonPressedColor = Skin.Accent2;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("shift_buttons")]
+ [ShifterName("Button foreground color")]
+ [ShifterDescription("Select the text and border color for each button.")]
+ public Color ButtonForegroundColor = Skin.DefaultForeground;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("shift_buttons")]
+ [ShifterName("Button border width")]
+ [ShifterDescription("Set the width, in pixels, of the button's border.")]
+ public int ButtonBorderWidth = 2;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("shift_buttons")]
+ [ShifterName("Button font")]
+ [ShifterDescription("Select the font for the button's text.")]
+ public Font ButtonTextFont = Skin.SysFont;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Buttons")]
+ [RequiresUpgrade("shift_buttons;skinning")]
+ [Image("buttonidle")]
+ [ShifterName("Button background color")]
+ [ShifterDescription("Select an image to show as the button's Idle state.")]
+ public byte[] ButtonBG = null;
+
+
+ [Image("panelclockbg")]
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel Clock")]
+ [ShifterName("Panel Clock Background Image")]
+ [ShifterDescription("Set the background image of the panel clock.")]
+ [RequiresUpgrade("skinning;shift_panel_clock")]
+ public byte[] PanelClockBG = null;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Login Screen")]
+ [RequiresUpgrade("gui_based_login_screen")]
+ [ShifterName("Login Screen Background Color")]
+ [ShifterDescription("Change the background color of the login screen.")]
+ public Color LoginScreenColor = Skin.DesktopBG;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Login Screen")]
+ [RequiresUpgrade("skinning;gui_based_login_screen")]
+ [ShifterName("Login Screen Background Image")]
+ [ShifterDescription("Set an image as your login screen!")]
+ [Image("login")]
+ public byte[] LoginScreenBG = null;
+
+
+ [RequiresUpgrade("shift_screensaver")]
+ [ShifterMeta("System")]
+ [ShifterCategory("Screen saver")]
+ [ShifterName("Screen saver wait (milliseconds)")]
+ [ShifterDescription("How long do you have to stay idle before the screensaver activates?")]
+ public int ScreensaverWait = 300000;
+
+ [RequiresUpgrade("skinning;shift_screensaver")]
+ [ShifterMeta("System")]
+ [ShifterCategory("Screen saver")]
+ [ShifterName("Screen saver image")]
+ [ShifterDescription("What image should appear on the screen saver?")]
+ public byte[] ScreensaverImage = null;
+
+
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [RequiresUpgrade("shift_title_text")]
+ [ShifterName("Title Font")]
+ [ShifterDescription("The font style for the title text.")]
+ public Font TitleFont = SysFont2;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("General")]
+ [ShifterName("System Font")]
+ [ShifterDescription("The font style used by the system.")]
+ public Font MainFont = SysFont;
+
+ [ShifterEnumMask(new[] { "Right", "Left" })]
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Title button position")]
+ [ShifterDescription("Where should the title buttons be located?")]
+ public int TitleButtonPosition = 0;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Header Fonts")]
+ [ShifterName("1st level header")]
+ [ShifterDescription("The font used in level 1 (title) headers.")]
+ public Font HeaderFont = Header1;
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Header Fonts")]
+ [ShifterName("2nd level header")]
+ [ShifterDescription("The font used in level 2 (subtitle) headers.")]
+ public Font Header2Font = Header2;
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("Header Fonts")]
+ [ShifterName("3rd level header")]
+ [ShifterDescription("The font used in level 3 (section) headers.")]
+ public Font Header3Font = Header3;
+
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("General")]
+ [ShifterName("System Background")]
+ [ShifterDescription("The background color of all system controls in the UI.")]
+ public Color ControlColor = DefaultBackground;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("General")]
+ [ShifterName("System Foreground")]
+ [ShifterDescription("The foreground color of all system controls in the UI.")]
+ public Color ControlTextColor = DefaultForeground;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [ShifterName("Title Text Color")]
+ [RequiresUpgrade("shift_title_text")]
+ [ShifterDescription("The color of the title text.")]
+ public Color TitleTextColor = TitleFG;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [ShifterName("Title Background Color")]
+ [RequiresUpgrade("shift_titlebar")]
+ [ShifterDescription("The color of the titlebar's background.")]
+ public Color TitleBackgroundColor = TitleBG;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [ShifterName("Left Border Background")]
+ [RequiresUpgrade("shift_window_borders")]
+ [ShifterDescription("The background color for the left border.")]
+ public Color BorderLeftBackground = TitleBG;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [ShifterName("Right Border Background")]
+ [RequiresUpgrade("shift_window_borders")]
+ [ShifterDescription("The background color for the right border.")]
+ public Color BorderRightBackground = TitleBG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel buttons")]
+ [RequiresUpgrade("shift_panel_buttons")]
+ [ShifterName("Panel buttons from top")]
+ [ShifterDescription("How far from the top should the panel buttons be?")]
+ public int PanelButtonFromTop = 2;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [ShifterName("Bottom Border Background")]
+ [RequiresUpgrade("shift_window_borders")]
+ [ShifterDescription("The background color for the bottom border.")]
+ public Color BorderBottomBackground = TitleBG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel buttons")]
+ [ShifterName("Panel button holder from left")]
+ [ShifterDescription("How far from the left should the panel button holder be?")]
+ [RequiresUpgrade("shift_panel_buttons")]
+ public int PanelButtonHolderFromLeft = 100;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [ShifterName("Bottom Left Border Background")]
+ [RequiresUpgrade("shift_window_borders")]
+ [ShifterDescription("The background color for the bottom left border.")]
+ public Color BorderBottomLeftBackground = TitleBG;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [ShifterName("Bottom Right Border Background")]
+ [RequiresUpgrade("shift_window_borders")]
+ [ShifterDescription("The background color for the bottom right border.")]
+ public Color BorderBottomRightBackground = TitleBG;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Close Button Color")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The close button color")]
+ public Color CloseButtonColor = Accent2;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Maximize Button Color")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The maximize button color")]
+ public Color MaximizeButtonColor = Accent1;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Minimize Button Color")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The minimize button color")]
+ public Color MinimizeButtonColor = Accent1;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Desktop Panel")]
+ [ShifterName("Panel Background")]
+ [RequiresUpgrade("shift_desktop_panel")]
+ [ShifterDescription("The background color used by the desktop panel")]
+ public Color DesktopPanelColor = TitleBG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Desktop Panel")]
+ [ShifterName("Panel Clock Text Color")]
+ [RequiresUpgrade("shift_panel_clock")]
+ [ShifterDescription("The text color used by the desktop panel's clock.")]
+ public Color DesktopPanelClockColor = TitleFG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Desktop Panel")]
+ [ShifterName("Panel Clock Background Color")]
+ [RequiresUpgrade("shift_panel_clock")]
+ [ShifterDescription("The background color used by the desktop panel's clock.")]
+ public Color DesktopPanelClockBackgroundColor = TitleBG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Desktop Panel")]
+ [ShifterName("Panel Clock Font")]
+ [RequiresUpgrade("shift_panel_clock")]
+ [ShifterDescription("The font used by the desktop panel's clock.")]
+ public Font DesktopPanelClockFont = Skin.SysFont2;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Desktop Panel")]
+ [ShifterName("Panel Clock From Right")]
+ [RequiresUpgrade("shift_panel_clock")]
+ [ShifterDescription("The position in pixels relative to the width of the desktop panel that the clock will sit at.")]
+ public Point DesktopPanelClockFromRight = new Point(2, 2);
+
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Desktop Panel")]
+ [ShifterName("Panel Height")]
+ [RequiresUpgrade("shift_desktop_panel")]
+ [ShifterDescription("The height in pixels of the desktop panel.")]
+ public int DesktopPanelHeight = 24;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Desktop Panel")]
+ [ShifterName("Panel Position")]
+ [ShifterEnumMask(new[] { "Top", "Bottom" })]
+ [RequiresUpgrade("shift_desktop_panel")]
+ [ShifterDescription("The position of the desktop panel.")]
+ public int DesktopPanelPosition = 0;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [ShifterName("Titlebar Height")]
+ [RequiresUpgrade("shift_titlebar")]
+ [ShifterDescription("The height of the titlebar.")]
+ public int TitlebarHeight = 30;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Close Button Size")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The close button size")]
+ public Size CloseButtonSize = new Size(24, 24);
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Maximize Button Size")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The maximize button size")]
+ public Size MaximizeButtonSize = new Size(24, 24);
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Minimize Button Size")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The minimize button size")]
+ public Size MinimizeButtonSize = new Size(24, 24);
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Close Button From Right")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The close button location from the right of the titlebar.")]
+ public Point CloseButtonFromSide = new Point(3, (30 - 24) / 2);
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Maximize Button From Right")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The maximize button location from the right of the titlebar.")]
+ public Point MaximizeButtonFromSide = new Point(24 + 6, (30 - 24) / 2);
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Minimize Button From Right")]
+ [RequiresUpgrade("shift_title_buttons")]
+ [ShifterDescription("The minimize button location from the right of the titlebar.")]
+ public Point MinimizeButtonFromSide = new Point(48 + 9, (30 - 24) / 2);
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [ShifterName("Title text centered?")]
+ [RequiresUpgrade("shift_title_text")]
+ [ShifterDescription("Is the title text centered?")]
+ public bool TitleTextCentered = false;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [ShifterName("Title Text From Left")]
+ [ShifterFlag("TitleTextCentered", false)]
+ [RequiresUpgrade("shift_title_text")]
+ [ShifterDescription("The title text location from the left of the titlebar.")]
+ public Point TitleTextLeft = new Point(4, 4);
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("General")]
+ [ShifterName("Desktop Background Color")]
+ [ShifterDescription("The background color of the desktop.")]
+ public Color DesktopColor = DesktopBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button select highlight")]
+ public Color Menu_ButtonSelectedHighlight = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button select border")]
+ public Color Menu_ButtonSelectedHighlightBorder = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button pressed highlight")]
+ public Color Menu_ButtonPressedHighlight = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button pressed border")]
+ public Color Menu_ButtonPressedHighlightBorder = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button checked highlight")]
+ public Color Menu_ButtonCheckedHighlight = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button checked border")]
+ public Color Menu_ButtonCheckedHighlightBorder = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button pressed gradient border")]
+ public Color Menu_ButtonPressedBorder = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button selected gradient border")]
+ public Color Menu_ButtonSelectedBorder = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button checked gradient start")]
+ public Color Menu_ButtonCheckedGradientBegin = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button checked gradient middle")]
+ public Color Menu_ButtonCheckedGradientMiddle = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button checked gradient end")]
+ public Color Menu_ButtonCheckedGradientEnd = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button selected gradient start")]
+ public Color Menu_ButtonSelectedGradientBegin = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button selected gradient middle")]
+ public Color Menu_ButtonSelectedGradientMiddle = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button selected gradient end")]
+ public Color Menu_ButtonSelectedGradientEnd = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button pressed gradient start")]
+ public Color Menu_ButtonPressedGradientBegin = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button pressed gradient middle")]
+ public Color Menu_ButtonPressedGradientMiddle = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Button pressed gradient end")]
+ public Color Menu_ButtonPressedGradientEnd = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Check BG")]
+ public Color Menu_CheckBackground = Skin.TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Check BG (Selected)")]
+ public Color Menu_CheckSelectedBackground = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Check BG (Pressed)")]
+ public Color Menu_CheckPressedBackground = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu bars")]
+ [ShifterName("Margin gradient start")]
+ public Color Menu_ImageMarginGradientBegin = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu bars")]
+ [ShifterName("Margin gradient middle")]
+ public Color Menu_ImageMarginGradientMiddle = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu bars")]
+ [ShifterName("Margin gradient end")]
+ public Color Menu_ImageMarginGradientEnd = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu bars")]
+ [ShifterName("Menu gradient start")]
+ public Color Menu_MenuStripGradientBegin = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu bars")]
+ [ShifterName("Menu gradient end")]
+ public Color Menu_MenuStripGradientEnd = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu bars")]
+ [ShifterName("Menu item selected")]
+ public Color Menu_MenuItemSelected = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu bars")]
+ [ShifterName("Menu item border")]
+ public Color Menu_MenuItemBorder = DefaultBackground;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu bars")]
+ [ShifterName("Menu border")]
+ public Color Menu_MenuBorder = DefaultBackground;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Menu item selected gradient start")]
+ public Color Menu_MenuItemSelectedGradientBegin = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Menu item selected gradient end")]
+ public Color Menu_MenuItemSelectedGradientEnd = Accent2;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Menu item pressed gradient start")]
+ public Color Menu_MenuItemPressedGradientBegin = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Menu item pressed gradient middle")]
+ public Color Menu_MenuItemPressedGradientMiddle = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Menu item pressed gradient end")]
+ public Color Menu_MenuItemPressedGradientEnd = Accent1;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Rafter gradient start")]
+ public Color Menu_RaftingContainerGradientBegin = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Rafter gradient end")]
+ public Color Menu_RaftingContainerGradientEnd = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Separator Color 1")]
+ public Color Menu_SeparatorDark = DefaultForeground;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Separator Color 2")]
+ public Color Menu_SeparatorLight = TitleFG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Status bars")]
+ [ShifterName("Status bar gradient start")]
+ public Color Menu_StatusStripGradientBegin = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Status bars")]
+ [ShifterName("Status bar gradient end")]
+ public Color Menu_StatusStripGradientEnd = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Toolbar Border")]
+ public Color Menu_ToolStripBorder = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Dropdown background")]
+ public Color Menu_ToolStripDropDownBackground = DefaultBackground;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Toolbar gradient start")]
+ public Color Menu_ToolStripGradientBegin = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Toolbar gradient middle")]
+ public Color Menu_ToolStripGradientMiddle = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Toolbars")]
+ [ShifterName("Toolbar gradient end")]
+ public Color Menu_ToolStripGradientEnd = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu holders")]
+ [ShifterName("Content panel gradient start")]
+ public Color Menu_ToolStripContentPanelGradientBegin = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu holders")]
+ [ShifterName("Content panel gradient end")]
+ public Color Menu_ToolStripContentPanelGradientEnd = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu holders")]
+ [ShifterName("Panel gradient start")]
+ public Color Menu_ToolStripPanelGradientBegin = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("Menu holders")]
+ [ShifterName("Panel gradient end")]
+ public Color Menu_ToolStripPanelGradientEnd = TitleBG;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Menu text color")]
+ public Color Menu_TextColor = DefaultForeground;
+
+ [ShifterMeta("Menus")]
+ [ShifterCategory("General")]
+ [ShifterName("Menu selected text color")]
+ public Color Menu_SelectedTextColor = TitleFG;
+
+
+
+ //Images
+ [Image("closebutton")]
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Close Button Image")]
+ [RequiresUpgrade("shift_title_buttons;skinning")]
+ [ShifterDescription("Show an image on the Close Button using this setting.")]
+ public byte[] CloseButtonImage = null;
+
+ [Image("minimizebutton")]
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Minimize Button Image")]
+ [RequiresUpgrade("shift_title_buttons;skinning")]
+ [ShifterDescription("Show an image on the Minimize Button using this setting.")]
+ public byte[] MinimizeButtonImage = null;
+
+ [Image("maximizebutton")]
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Title Buttons")]
+ [ShifterName("Maximize Button Image")]
+ [RequiresUpgrade("shift_title_buttons;skinning")]
+ [ShifterDescription("Show an image on the Maximize Button using this setting.")]
+ public byte[] MaximizeButtonImage = null;
+
+ [Image("desktopbackground")]
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("General")]
+ [ShifterName("Desktop Background Image")]
+ [RequiresUpgrade("skinning")]
+ [ShifterDescription("Use an image as your desktop background.")]
+ public byte[] DesktopBackgroundImage = null;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_app_launcher")]
+ [ShifterName("App Launcher Text Color")]
+ [ShifterDescription("Change the color of the App Launcher text.")]
+ public Color AppLauncherTextColor = Skin.DefaultForeground;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_app_launcher")]
+ [ShifterName("App Launcher Selected Text Color")]
+ [ShifterDescription("Change the color of the app launcher's text while it is selected.")]
+ public Color AppLauncherSelectedTextColor = Skin.TitleFG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_app_launcher")]
+ [ShifterName("App Launcher Font")]
+ [ShifterDescription("Change the font that the App Launcher text is displayed in.")]
+ public Font AppLauncherFont = Skin.SysFont2;
+
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [ShifterName("App launcher text")]
+ [ShifterDescription("The text displayed on the app launcher.")]
+ [RequiresUpgrade("shift_app_launcher")]
+ public string AppLauncherText = "ShiftOS";
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [ShifterName("App launcher from left")]
+ [ShifterDescription("The position of the app launcher from the left of the Desktop Panel.")]
+ [RequiresUpgrade("shift_app_launcher")]
+ public Point AppLauncherFromLeft = new Point(0, 0);
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [ShifterName("App launcher size")]
+ [ShifterDescription("The size of the app launcher.")]
+ [RequiresUpgrade("shift_app_launcher")]
+ public Size AppLauncherHolderSize = new Size(100, 24);
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [ShifterName("App launcher image")]
+ [ShifterDescription("The image that will appear on the app launcher.")]
+ [Image("applauncher")]
+ [RequiresUpgrade("skinning;shift_app_launcher")]
+ public byte[] AppLauncherImage = null;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("General")]
+ [ShifterName("Terminal font")]
+ public Font TerminalFont = new Font("Lucida Console", 9F, FontStyle.Regular);
+
+ [ShifterMeta("System")]
+ [ShifterCategory("General")]
+ [ShifterName("Terminal text color")]
+ public ConsoleColor TerminalForeColorCC = ConsoleColor.White;
+
+ [ShifterMeta("System")]
+ [ShifterCategory("General")]
+ [ShifterName("Terminal background color")]
+ public ConsoleColor TerminalBackColorCC = ConsoleColor.Black;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Desktop Panel")]
+ [ShifterName("Panel background image")]
+ [Image("desktoppanel")]
+ [RequiresUpgrade("skinning;shift_desktop_panel")]
+ public byte[] DesktopPanelBackground = null;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [ShifterName("Titlebar background image")]
+ [Image("titlebar")]
+ [RequiresUpgrade("skinning;shift_titlebar")]
+ public byte[] TitleBarBackground = null;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [RequiresUpgrade("shift_titlebar")]
+ [ShifterName("Show title corners?")]
+ [ShifterDescription("If checked, a left and a right section will appear on the titlebar which is useful for rounded corners, padding, or other useful properties.")]
+ public bool ShowTitleCorners = false;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [RequiresUpgrade("shift_titlebar")]
+ [ShifterFlag("ShowTitleCorners", true)]
+ [ShifterName("Title left background color")]
+ [ShifterDescription("What color should be used for the left title corner?")]
+ public Color TitleLeftCornerBackground = TitleBG;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [RequiresUpgrade("shift_titlebar")]
+ [ShifterFlag("ShowTitleCorners", true)]
+ [ShifterName("Title right background color")]
+ [ShifterDescription("What color should be used for the right title corner?")]
+ public Color TitleRightCornerBackground = TitleBG;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [RequiresUpgrade("shift_titlebar")]
+ [ShifterFlag("ShowTitleCorners", true)]
+ [ShifterName("Title left corner width")]
+ [ShifterDescription("How wide should the left title corner be?")]
+ public int TitleLeftCornerWidth = 2;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [RequiresUpgrade("shift_titlebar")]
+ [ShifterFlag("ShowTitleCorners", true)]
+ [ShifterName("Title right corner width")]
+ [ShifterDescription("How wide should the right title corner be?")]
+ public int TitleRightCornerWidth = 2;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [RequiresUpgrade("skinning;shift_titlebar")]
+ [ShifterFlag("ShowTitleCorners", true)]
+ [ShifterName("Title left corner background image")]
+ [ShifterDescription("Select an image to appear as the background texture for the left titlebar corner.")]
+ [Image("titleleft")]
+ public byte[] TitleLeftBG = null;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [RequiresUpgrade("skinning;shift_titlebar")]
+ [ShifterFlag("ShowTitleCorners", true)]
+ [ShifterName("Title right corner background image")]
+ [ShifterDescription("Select an image to appear as the background texture for the right titlebar corner.")]
+ [Image("titleright")]
+ public byte[] TitleRightBG = null;
+
+
+ [ShifterMeta("System")]
+ [ShifterCategory("General")]
+ [ShifterName("System color key-out")]
+ [ShifterDescription("This is a color that will be represented as \"transparent\" in windows. This does not affect the desktop.")]
+ public Color SystemKey = Color.FromArgb(1, 0, 1);
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [RequiresUpgrade("skinning;shift_window_borders")]
+ [Image("bottomborder")]
+ [ShifterName("Bottom Border Image")]
+ [ShifterDescription("Select an image to appear on the bottom border.")]
+ public byte[] BottomBorderBG = null;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [RequiresUpgrade("skinning;shift_window_borders")]
+ [Image("bottomrborder")]
+ [ShifterName("Bottom Right Border Image")]
+ [ShifterDescription("Select an image to appear on the bottom right border.")]
+ public byte[] BottomRBorderBG = null;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [RequiresUpgrade("skinning;shift_window_borders")]
+ [Image("bottomlborder")]
+ [ShifterName("Bottom Left Border Image")]
+ [ShifterDescription("Select an image to appear on the bottom left border.")]
+ public byte[] BottomLBorderBG = null;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [RequiresUpgrade("skinning;shift_window_borders")]
+ [Image("leftborder")]
+ [ShifterName("Left Border Image")]
+ [ShifterDescription("Select an image to appear on the left border.")]
+ public byte[] LeftBorderBG = null;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [RequiresUpgrade("skinning;shift_window_borders")]
+ [Image("rightborder")]
+ [ShifterName("Right Border Image")]
+ [ShifterDescription("Select an image to appear on the right border.")]
+ public byte[] RightBorderBG = null;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [RequiresUpgrade("shift_window_borders")]
+ [ShifterName("Left border width")]
+ [ShifterDescription("How wide should the left border be?")]
+ public int LeftBorderWidth = 2;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [RequiresUpgrade("shift_window_borders")]
+ [ShifterName("Right border width")]
+ [ShifterDescription("How wide should the right border be?")]
+ public int RightBorderWidth = 2;
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Window border")]
+ [RequiresUpgrade("shift_window_borders")]
+ [ShifterName("Bottom border height")]
+ [ShifterDescription("How tall should the bottom border be?")]
+ public int BottomBorderWidth = 2;
+
+ [Image("panelbutton")]
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel buttons")]
+ [RequiresUpgrade("skinning;shift_panel_buttons")]
+ [ShifterName("Panel button background image")]
+ [ShifterDescription("Select a texture to display as the panel button background.")]
+ public byte[] PanelButtonBG = null;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel buttons")]
+ [RequiresUpgrade("shift_panel_buttons")]
+ [ShifterName("Panel button size")]
+ [ShifterDescription("How big should the panel button be?")]
+ public Size PanelButtonSize = new Size(185, 20);
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel buttons")]
+ [RequiresUpgrade("shift_panel_buttons")]
+ [ShifterName("Panel button background color")]
+ [ShifterDescription("Select a background color for the panel button.")]
+ public Color PanelButtonColor = Skin.Accent2;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel buttons")]
+ [RequiresUpgrade("shift_panel_buttons")]
+ [ShifterName("Panel button text color")]
+ [ShifterDescription("Select a text color for the panel button.")]
+ public Color PanelButtonTextColor = Skin.TitleFG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel buttons")]
+ [RequiresUpgrade("shift_panel_buttons")]
+ [ShifterName("Panel button text from left")]
+ [ShifterDescription("The position relative to the panel button left in pixels that the text is placed at.")]
+ public Point PanelButtonFromLeft = new Point(2, 2);
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("Panel buttons")]
+ [RequiresUpgrade("shift_panel_buttons")]
+ [ShifterName("Panel button font")]
+ [ShifterDescription("Select a font for the panel button.")]
+ public Font PanelButtonFont = Skin.SysFont2;
+
+
+ //we DO NOT want this showing in the shifter.
+ [ShifterHidden]
+ public Dictionary<string, ImageLayout> SkinImageLayouts = new Dictionary<string, ImageLayout>();
+
+ [ShifterMeta("Windows")]
+ [ShifterCategory("Titlebar")]
+ [ShifterName("App icon from side")]
+ [ShifterDescription("How far from the side should the icon be?")]
+ [RequiresUpgrade("shift_titlebar;app_icons")]
+ public Point TitlebarIconFromSide = new Point(4, 4);
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("Status Panel Font")]
+ [ShifterDescription("The font used by the status panel in the Advanced App Launcher.")]
+ public Font ALStatusPanelFont = SysFont;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("Status Panel Text Color")]
+ [ShifterDescription("The text color for the AL status panel.")]
+ public Color ALStatusPanelTextColor = Skin.DefaultForeground;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("Status Panel Background")]
+ [ShifterDescription("The status panel's background color.")]
+ public Color ALStatusPanelBackColor = TitleBG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("Status Panel Text Alignment")]
+ [ShifterDescription("What part of the panel should the status text stick to?")]
+ public ContentAlignment ALStatusPanelAlignment = ContentAlignment.MiddleCenter;
+
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("AL System Status Height")]
+ [ShifterDescription("Set the height of the top system status bar in the App Launcher.")]
+ public int ALSystemStatusHeight = 50;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("AL Size")]
+ [ShifterDescription("Set the size of the App Launcher's container")]
+ public Size AALSize = new Size(425, 500);
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("AL Category View Width")]
+ [ShifterDescription("Set the width of the left Category Listing on the app launcher.")]
+ public int AALCategoryViewWidth = 237;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("AL Item List Width")]
+ [ShifterDescription("Set the width of the item list in the app launcher.")]
+ public int AALItemViewWidth = 237;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("AL System Actions Height")]
+ [ShifterDescription("Set the height of the bottom system actions bar in the App Launcher.")]
+ public int ALSystemActionHeight = 30;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("skinning;shift_advanced_app_launcher")]
+ [ShifterName("Status Panel Background Image")]
+ [ShifterDescription("Use an image for the App Launcher Status Panel")]
+ [Image("al_bg_status")]
+ public byte[] ALStatusPanelBG = null;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterEnumMask(new[] { "Button, bottom panel", "Button, top panel", "Category Item" })]
+ [ShifterName("Shutdown Button position")]
+ [ShifterDescription("Change the position and layout of the App Launcher Shutdown button.")]
+ public int ShutdownButtonStyle = 0;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("Shutdown Button from side")]
+ [ShifterDescription("The location relative to the side of the system panel that the shutdown button should reside from.")]
+ public Point ShutdownButtonFromSide = new Point(2, 2);
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("Align shutdown button to left?")]
+ [ShifterDescription("Should the location of the shutdown button be calculated relative to the left of the system panel?")]
+ public bool ShutdownOnLeft = false;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("Shutdown Button Font")]
+ [ShifterDescription("The font of the Shutdown Button")]
+ public Font ShutdownFont = SysFont;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("Shutdown Text Color")]
+ [ShifterDescription("The foreground color of the Shutdown button")]
+ public Color ShutdownForeColor = DefaultForeground;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("System Panel background color")]
+ [ShifterDescription("The background color of the App Launcher System Panel.")]
+ public Color SystemPanelBackground = TitleBG;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("skinning;shift_advanced_app_launcher")]
+ [ShifterName("System Panel Background Image")]
+ [ShifterDescription("The background image of the System Panel.")]
+ [Image("al_bg_system")]
+ public byte[] SystemPanelBG = null;
+
+ [ShifterMeta("Desktop")]
+ [ShifterCategory("App Launcher")]
+ [RequiresUpgrade("shift_advanced_app_launcher")]
+ [ShifterName("App Launcher Item Font")]
+ [ShifterDescription("Select the font to use for the items in the App Launcher.")]
+ public Font AdvALItemFont = SysFont2;
+ }
+
+ /// <summary>
+ /// Marks a skin spec field as hidden from the Shifter.
+ /// </summary>
+ public class ShifterHiddenAttribute : Attribute
+ {
+
+ }
+
+ public class ShifterFlagAttribute : Attribute
+ {
+ public ShifterFlagAttribute(string flag, bool expected)
+ {
+ Expected = expected;
+ Flag = flag;
+ }
+
+ public bool Expected { get; set; }
+ public string Flag { get; set; }
+ public bool IsTrue(Skin skn)
+ {
+ foreach (var f in skn.GetType().GetFields())
+ {
+ if (f.Name == Flag)
+ {
+ if (f.FieldType == typeof(bool))
+ {
+ return (bool)f.GetValue(skn) == Expected;
+ }
+ }
+ }
+ throw new ArgumentException("The flag attribute was given an incorrect flag variable name.");
+ }
+ }
+
+ public class ImageAttribute : Attribute
+ {
+ /// <summary>
+ /// Attribute a byte array within the Skin object with this attribute and the engine and Shifter will see it as an image and you'll be able to grab the image by calling SkinEngine.GetImage() passing the name you input here.
+ /// </summary>
+ /// <param name="name">The name you want to reference this image as in the code.</param>
+ public ImageAttribute(string name)
+ {
+ Name = name;
+ }
+
+ public string Name { get; set; }
+ }
+
+
+ public class ShifterEnumMaskAttribute : Attribute
+ {
+ public ShifterEnumMaskAttribute(string[] items)
+ {
+ Items = items;
+ }
+
+ public string[] Items { get; set; }
+ }
+
+
+
+ public class ShifterNameAttribute : Attribute
+ {
+ public ShifterNameAttribute(string name)
+ {
+ Name = name;
+ }
+
+ public string Name { get; set; }
+ }
+
+ public class ShifterDescriptionAttribute : Attribute
+ {
+ public ShifterDescriptionAttribute(string description)
+ {
+ Description = description;
+ }
+
+ public string Description { get; set; }
+ }
+
+ public class ShifterCategoryAttribute : Attribute
+ {
+
+ public ShifterCategoryAttribute(string category)
+ {
+ Category = category;
+ }
+
+ public string Category { get; set; }
+ }
+
+ public interface ISkinPostProcessor
+ {
+ /// <summary>
+ /// Perform algorithmic operations at the bit level on a ShiftOS skin image.
+ /// </summary>
+ /// <param name="original">The image, as loaded by the engine, as a 32-bit ARGB byte array.</param>
+ /// <returns>The processed image.</returns>
+ byte[] ProcessImage(byte[] original);
+ }
+
+ public class ShifterMetaAttribute : Attribute
+ {
+
+ public ShifterMetaAttribute(string meta)
+ {
+ Meta = meta;
+ }
+
+ public string Meta { get; set; }
+ }
+} \ No newline at end of file
diff --git a/ShiftOS_TheReturn/Story.cs b/ShiftOS_TheReturn/Story.cs
new file mode 100644
index 0000000..bcee49c
--- /dev/null
+++ b/ShiftOS_TheReturn/Story.cs
@@ -0,0 +1,119 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace ShiftOS.Engine
+{
+ /// <summary>
+ /// Storyline management class.
+ /// </summary>
+ public static class Story
+ {
+ /// <summary>
+ /// Starts the storyline with the specified Storyline ID.
+ /// </summary>
+ /// <param name="stid">The storyline ID to start.</param>
+ public static void Start(string stid)
+ {
+ foreach (var exec in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ if(exec.EndsWith(".exe") || exec.EndsWith(".dll"))
+ {
+ try
+ {
+ if (SaveSystem.CurrentSave.StoriesExperienced == null)
+ SaveSystem.CurrentSave.StoriesExperienced = new List<string>();
+ var asm = Assembly.LoadFile(exec);
+ foreach(var type in asm.GetTypes())
+ {
+ foreach(var mth in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
+ {
+ foreach(var attrib in mth.GetCustomAttributes(false))
+ {
+ if(attrib is StoryAttribute)
+ {
+ var story = attrib as StoryAttribute;
+ if(story.StoryID == stid)
+ {
+ mth.Invoke(null, null);
+ SaveSystem.CurrentSave.StoriesExperienced.Add(stid);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex) { throw ex; }
+ }
+ }
+#if DEBUG
+ throw new ArgumentException("Story ID not found: " + stid + " - Talk to Michael. NOW.");
+#else
+ Debug.Print("No such story: " + stid);
+#endif
+ }
+
+ [Obsolete("Please use Story.Start() in tandem with [StoryAttribute].")]
+ public static void RunFromInternalResource(string resource_id)
+ {
+ }
+
+
+ }
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+ public class StoryAttribute : Attribute
+ {
+ /// <summary>
+ /// Creates a new instance of the <see cref="StoryAttribute"/> attribute.
+ /// </summary>
+ /// <param name="id">The ID of this story plot.</param>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="StoryAttribute"/> is used to turn a static, public method into a story element. Using the specified <paramref name="id"/> argument, the ShiftOS Engine can determine whether this plot has already been experienced, and using the <see cref="Shiftorium"/> classes, the ID is treated as a special Shiftorium upgrade, and you can use the <see cref="RequiresUpgradeAttribute"/> attribute as well as the various other ways of determining whether a Shiftorium upgrade is installed to determine if this plot has been experienced.
+ /// </para>
+ /// </remarks>
+ public StoryAttribute(string id)
+ {
+ StoryID = id;
+ }
+
+ /// <summary>
+ /// Gets the storyline ID stored in this attribute.
+ /// </summary>
+ public string StoryID { get; private set; }
+
+ }
+}
diff --git a/ShiftOS_TheReturn/TerminalBackend.cs b/ShiftOS_TheReturn/TerminalBackend.cs
new file mode 100644
index 0000000..79c6349
--- /dev/null
+++ b/ShiftOS_TheReturn/TerminalBackend.cs
@@ -0,0 +1,623 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using static ShiftOS.Engine.SaveSystem;
+
+namespace ShiftOS.Engine
+{
+ /// <summary>
+ /// Backend for the ShiftOS terminal.
+ /// </summary>
+ public static class TerminalBackend
+ {
+ /// <summary>
+ /// Occurs when a command is processed.
+ /// </summary>
+ public static event Action<string, string> CommandProcessed;
+
+ /// <summary>
+ /// Gets or sets whether the current command is elevated.
+ /// </summary>
+ public static bool Elevated { get; set; }
+
+ /// <summary>
+ /// Parses command-line arguments using the ShiftOS syntax and stores them in a <see cref="Dictionary{string, string}"/>, removing the parsed text from the original string.
+ /// </summary>
+ /// <param name="text">The text to parse.</param>
+ /// <returns><see cref="Dictionary{string, string}"/> containing the parsed arguments.</returns>
+ public static Dictionary<string, string> GetArgs(ref string text)
+ {
+ bool shouldParse = false;
+ int argStart = 0;
+ if (text.Contains("{"))
+ {
+ shouldParse = true;
+ argStart = text.IndexOf('{');
+ }
+
+ if (shouldParse == false)
+ {
+ string replacement = Regex.Replace(text, @"\t|\n|\r", "");
+ text = replacement + "{}";
+ shouldParse = true;
+ argStart = text.IndexOf('{');
+ }
+
+ string args = text.Substring(argStart, text.Length - argStart);
+
+ text = text.Remove(argStart, text.Length - argStart).Replace(" ", "");
+ return JsonConvert.DeserializeObject<Dictionary<string, string>>(args);
+ }
+
+ /// <summary>
+ /// String representing the last command entered by the user.
+ /// </summary>
+ public static string LastCommand = "";
+
+ /// <summary>
+ /// Invokes a ShiftOS terminal command.
+ /// </summary>
+ /// <param name="ns">The command's namespace.</param>
+ /// <param name="command">The command name.</param>
+ /// <param name="arguments">The command arguments.</param>
+ /// <param name="isRemote">Whether the command should be sent through Remote Terminal Session (RTS).</param>
+ public static void InvokeCommand(string ns, string command, Dictionary<string, string> arguments, bool isRemote = false)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(ns))
+ return;
+
+
+ bool commandWasClient = RunClient(ns, command, arguments, isRemote);
+
+ if (!commandWasClient && !string.IsNullOrWhiteSpace(ns))
+ {
+ PrefixEnabled = false;
+
+ ServerManager.SendMessage("script", $@"{{
+ user: ""{ns}"",
+ script: ""{command}"",
+ args: ""{GetSentArgs(arguments)}""
+}}");
+ }
+
+ CommandProcessed?.Invoke(ns + "." + command, JsonConvert.SerializeObject(arguments));
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Command parse error: {ex.Message}"); // This shouldn't ever be called now
+ PrefixEnabled = true;
+
+ }
+ }
+
+ /// <summary>
+ /// Transforms a <see cref="Dictionary{String, String}"/> of arguments to a <see cref="Dictionary{String, Object}"/>.
+ /// </summary>
+ /// <param name="argss">The original argument dictionary to convert.</param>
+ /// <returns>The converted dictionary.</returns>
+ public static string GetSentArgs(Dictionary<string, string> argss)
+ {
+ Dictionary<string, object> args = new Dictionary<string, object>();
+ foreach (KeyValuePair<string, string> arg in argss)
+ {
+ args[arg.Key] = arg.Value;
+ }
+ return JsonConvert.SerializeObject(args);
+ }
+
+ /// <summary>
+ /// Invokes a ShiftOS terminal command.
+ /// </summary>
+ /// <param name="text">The full command text in regular ShiftOS syntax</param>
+ /// <param name="isRemote">Whether the command should be sent through Remote Terminal Session (RTS).</param>
+ public static void InvokeCommand(string text, bool isRemote = false)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(text))
+ return;
+
+ var args = GetArgs(ref text);
+
+ bool commandWasClient = RunClient(text, args, isRemote);
+
+ if (!commandWasClient)
+ {
+ PrefixEnabled = false;
+
+ ServerManager.SendMessage("script", $@"{{
+ user: ""{text.Split('.')[0]}"",
+ script: ""{text.Split('.')[1]}"",
+ args: ""{GetSentArgs(args)}""
+}}");
+ }
+ CommandProcessed?.Invoke(text, GetSentArgs(args));
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Command parse error: {ex.Message}");
+ PrefixEnabled = true;
+
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets whether the user prefix is printed after a command completes.
+ /// </summary>
+ public static bool PrefixEnabled { get; set; }
+
+ /// <summary>
+ /// Gets or sets whether the user is in a story plot, and thus, the terminal input should be disabled.
+ /// </summary>
+ public static bool InStory { get; set; }
+
+ /// <summary>
+ /// Another latest command string.
+ /// </summary>
+ public static string latestCommmand = "";
+
+ /// <summary>
+ /// Occurs when the engine requests a Terminal to be open.
+ /// </summary>
+ public static event EmptyEventHandler TerminalRequested;
+
+ /// <summary>
+ /// Opens a Terminal.
+ /// </summary>
+ internal static void OpenTerminal()
+ {
+ TerminalRequested?.Invoke();
+ }
+
+ /// <summary>
+ /// Determines if the specified command method can be ran in RTS
+ /// </summary>
+ /// <param name="mth">The method to scan</param>
+ /// <param name="isRemote">Is the user in an RTS session?</param>
+ /// <returns>Whether the command can be run.</returns>
+ public static bool CanRunRemotely(MethodInfo mth, bool isRemote)
+ {
+ if (!isRemote)
+ return true;
+
+ foreach (var attr in mth.GetCustomAttributes(false))
+ {
+ if (attr is RemoteLockAttribute)
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Runs a command on the client-side.
+ /// </summary>
+ /// <param name="ns">The command's namespace.</param>
+ /// <param name="cmd">The command name.</param>
+ /// <param name="args">The command's arguments.</param>
+ /// <param name="isRemote">Whether the command should be ran through RTS.</param>
+ /// <returns>Whether the command ran successfully.</returns>
+ public static bool RunClient(string ns, string cmd, Dictionary<string, string> args, bool isRemote = false)
+ {
+ return RunClient(ns + "." + cmd, args, isRemote);
+ }
+
+ /// <summary>
+ /// Runs a command on the client.
+ /// </summary>
+ /// <param name="text">The command text.</param>
+ /// <param name="argss">The command arguments.</param>
+ /// <param name="isRemote">Whether the command should be ran through RTS.</param>
+ /// <returns>Whether the command ran successfully.</returns>
+ public static bool RunClient(string text, Dictionary<string, string> argss, bool isRemote = false)
+ {
+ Dictionary<string, object> args = new Dictionary<string, object>();
+ foreach (KeyValuePair<string, string> arg in argss)
+ {
+ args[arg.Key] = arg.Value;
+ }
+ return RunClient(text, args, isRemote);
+ }
+
+ /// <summary>
+ /// Runs a command on the client.
+ /// </summary>
+ /// <param name="text">The command text.</param>
+ /// <param name="args">The command arguments.</param>
+ /// <param name="isRemote">Whether the command should be run in RTS.</param>
+ /// <returns>Whether the command ran successfully.</returns>
+ public static bool RunClient(string text, Dictionary<string, object> args, bool isRemote = false)
+ {
+ latestCommmand = text;
+
+ //Console.WriteLine(text + " " + "{" + string.Join(",", args.Select(kv => kv.Key + "=" + kv.Value).ToArray()) + "}" + " " + isRemote);
+
+ foreach (var asmExec in System.IO.Directory.GetFiles(Environment.CurrentDirectory))
+ {
+ try
+ {
+ var asm = Assembly.LoadFile(asmExec);
+
+ var types = asm.GetTypes();
+ foreach (var type in types)
+ {
+ if (Shiftorium.UpgradeAttributesUnlocked(type))
+ {
+ foreach (var a in type.GetCustomAttributes(false))
+ {
+ if (a is Namespace)
+ {
+ var ns = a as Namespace;
+ if (text.Split('.')[0] == ns.name)
+ {
+ if (KernelWatchdog.IsSafe(type))
+ {
+ if (KernelWatchdog.CanRunOffline(type))
+ {
+ foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
+ {
+ if (Shiftorium.UpgradeAttributesUnlocked(method))
+ {
+ foreach (var ma in method.GetCustomAttributes(false))
+ {
+ if (ma is Command)
+ {
+ var cmd = ma as Command;
+ if (text.Split('.')[1] == cmd.name)
+ {
+ if (KernelWatchdog.IsSafe(method))
+ {
+ if (KernelWatchdog.CanRunOffline(method))
+ {
+ var attr = method.GetCustomAttribute<CommandObsolete>();
+
+ if (attr != null)
+ {
+ string newcommand = attr.newcommand;
+ if (attr.warn)
+ {
+ Console.WriteLine(Localization.Parse((newcommand == "" ? "{ERROR}" : "{WARN}") + attr.reason, new Dictionary<string, string>() {
+ {"%newcommand", newcommand}
+ }));
+ }
+ if (newcommand != "")
+ {
+ // redo the entire process running newcommand
+
+ return RunClient(newcommand, args);
+ }
+ }
+
+ var requiresArgs = method.GetCustomAttributes<RequiresArgument>();
+ bool error = false;
+ bool providedusage = false;
+
+ foreach (RequiresArgument argument in requiresArgs)
+ {
+ if (!args.ContainsKey(argument.argument))
+ {
+
+ if (!providedusage)
+ {
+ string usageparse = "{COMMAND_" + ns.name.ToUpper() + "_" + cmd.name.ToUpper() + "_USAGE}";
+ if (usageparse == Localization.Parse(usageparse))
+ usageparse = "";
+ else
+ usageparse = Shiftorium.UpgradeInstalled("help_usage") ? Localization.Parse("{ERROR}{USAGE}" + usageparse, new Dictionary<string, string>() {
+ {"%ns", ns.name},
+ {"%cmd", cmd.name}
+ }) : "";
+
+ Console.WriteLine(usageparse);
+
+ providedusage = true;
+ }
+ if (Shiftorium.UpgradeInstalled("help_usage"))
+ {
+ Console.WriteLine(Localization.Parse("{ERROR_ARGUMENT_REQUIRED}", new Dictionary<string, string>() {
+ {"%argument", argument.argument}
+ }));
+ }
+ else
+ {
+ Console.WriteLine(Localization.Parse("{ERROR_ARGUMENT_REQUIRED_NO_USAGE}"));
+ }
+
+ error = true;
+ }
+ }
+
+ if (error)
+ {
+ throw new Exception("{ERROR_COMMAND_WRONG}");
+ }
+
+ try
+ {
+ return (bool)method.Invoke(null, new[] { args });
+ }
+ catch (TargetInvocationException e)
+ {
+ Console.WriteLine(Localization.Parse("{ERROR_EXCEPTION_THROWN_IN_METHOD}"));
+ Console.WriteLine(e.InnerException.Message);
+ Console.WriteLine(e.InnerException.StackTrace);
+ return true;
+ }
+ catch
+ {
+ return (bool)method.Invoke(null, new object[] { });
+ }
+ }
+ else
+ {
+ Console.Write("<");
+ ConsoleEx.Bold = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkRed;
+ Console.Write("session_mgr");
+ ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC;
+ ConsoleEx.Bold = false;
+ Console.Write(">");
+ ConsoleEx.Italic = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine(" You cannot run this command while disconnected from the multi-user domain..");
+ return true;
+
+ }
+ }
+ else
+ {
+ if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Admin)
+ {
+ Infobox.PromptText("Elevate to root mode", "This command cannot be run as a regular user. To run this command, please enter your password to elevate to root mode temporarily.", (pass) =>
+ {
+ if (pass == SaveSystem.CurrentUser.Password)
+ {
+ KernelWatchdog.EnterKernelMode();
+ RunClient(text, args, isRemote);
+ KernelWatchdog.LeaveKernelMode();
+ }
+ else
+ {
+ Infobox.Show("Access denied.", "You did not type in the correct password.");
+ }
+ }, true);
+ return true;
+ }
+ Console.Write("<");
+ ConsoleEx.Bold = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkRed;
+ Console.Write("watchdog");
+ ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC;
+ ConsoleEx.Bold = false;
+ Console.Write(">");
+ ConsoleEx.Italic = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine(" You cannot run this command. You do not have permission. Incident reported.");
+ KernelWatchdog.Log("potential_sys_breach", "user attempted to run kernel mode command " + text + " - watchdog has prevented this, good sir.");
+ return true;
+ }
+ }
+ }
+
+
+ }
+ }
+
+ else
+ {
+ Console.Write("<");
+ ConsoleEx.Bold = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkRed;
+ Console.Write("session_mgr");
+ ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC;
+ ConsoleEx.Bold = false;
+ Console.Write(">");
+ ConsoleEx.Italic = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine(" You cannot run this command while disconnected from the multi-user domain..");
+ return true;
+
+ }
+
+ }
+ }
+ else
+ {
+ if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Admin)
+ {
+ Infobox.PromptText("Elevate to root mode", "This command cannot be run as a regular user. To run this command, please enter your password to elevate to root mode temporarily.", (pass) =>
+ {
+ if (pass == SaveSystem.CurrentUser.Password)
+ {
+ KernelWatchdog.EnterKernelMode();
+ RunClient(text, args, isRemote);
+ KernelWatchdog.LeaveKernelMode();
+ }
+ else
+ {
+ Infobox.Show("Access denied.", "You did not type in the correct password.");
+ }
+ }, true);
+ return true;
+ }
+ Console.Write("<");
+ ConsoleEx.Bold = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkRed;
+ Console.Write("watchdog");
+ ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC;
+ ConsoleEx.Bold = false;
+ Console.Write(">");
+ ConsoleEx.Italic = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine(" You cannot run this command. You do not have permission. Incident reported.");
+ KernelWatchdog.Log("potential_sys_breach", "user attempted to run kernel mode command " + text + " - watchdog has prevented this, good sir.");
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch { }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Prints the user prompt to the terminal.
+ /// </summary>
+ public static void PrintPrompt()
+ {
+ if (SaveSystem.CurrentSave != null && CurrentUser != null)
+ {
+ Desktop.InvokeOnWorkerThread(() =>
+ {
+ ConsoleEx.BackgroundColor = SkinEngine.LoadedSkin.TerminalBackColorCC;
+ ConsoleEx.Italic = false;
+ ConsoleEx.Underline = false;
+
+ ConsoleEx.ForegroundColor = ConsoleColor.Magenta;
+ ConsoleEx.Bold = true;
+
+ Console.Write(SaveSystem.CurrentUser.Username);
+ ConsoleEx.Bold = false;
+ ConsoleEx.ForegroundColor = ConsoleColor.Gray;
+ Console.Write("@");
+ ConsoleEx.Italic = true;
+ ConsoleEx.Bold = true;
+ ConsoleEx.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write(SaveSystem.CurrentSave.SystemName);
+ ConsoleEx.Italic = false;
+ ConsoleEx.Bold = false;
+ ConsoleEx.ForegroundColor = ConsoleColor.Gray;
+ Console.Write(":~");
+ Console.ForegroundColor = ConsoleColor.White;
+ ConsoleEx.Italic = true;
+ if (KernelWatchdog.InKernelMode == true)
+ Console.Write("#");
+ else
+ Console.Write("$");
+ ConsoleEx.Italic = false;
+ ConsoleEx.Bold = false;
+ ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC;
+ Console.Write(" ");
+ });
+ }
+ }
+
+ /// <summary>
+ /// Static constructor for <see cref="TerminalBackend"/>.
+ /// </summary>
+ static TerminalBackend()
+ {
+ ServerMessageReceived onMessageReceived = (msg) =>
+ {
+ if (msg.Name == "trm_invokecommand")
+ {
+ string text3 = "";
+ string text4 = msg.Contents;
+
+ if (TerminalBackend.PrefixEnabled)
+ {
+ text3 = text4.Remove(0, $"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ ".Length);
+ }
+ IsForwardingConsoleWrites = true;
+ if (TerminalBackend.InStory == false)
+ {
+ TerminalBackend.InvokeCommand(text3, true);
+ }
+ if (TerminalBackend.PrefixEnabled)
+ {
+ Console.Write($"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ ");
+ }
+ IsForwardingConsoleWrites = false;
+ }
+ else if (msg.Name == "pleasewrite")
+ {
+ Console.Write(msg.Contents);
+ }
+ else if (msg.Name == "handshake_from")
+ {
+ var a = JsonConvert.DeserializeObject<Dictionary<string, object>>(msg.Contents);
+ string uName = a["username"] as string;
+ string pass = a["password"] as string;
+ string sys = a["sysname"] as string;
+ string guid = msg.GUID;
+ if (SaveSystem.CurrentSave.Username == uName && SaveSystem.CurrentSave.Password == pass && CurrentSave.SystemName == sys)
+ {
+ ForwardGUID = guid;
+ ServerManager.SendMessage("trm_handshake_accept", $@"{{
+ guid: ""{ServerManager.thisGuid}"",
+ target: ""{guid}""
+}}");
+
+ IsForwardingConsoleWrites = true;
+ InvokeCommand("sos.status");
+ Console.Write($"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ ");
+ IsForwardingConsoleWrites = false;
+ }
+ }
+ };
+
+ ServerManager.MessageReceived += onMessageReceived;
+ }
+
+ /// <summary>
+ /// Gets whether the terminal backend is forwarding console write requests through RTS to a remote client.
+ /// </summary>
+ public static bool IsForwardingConsoleWrites { get; internal set; }
+
+ /// <summary>
+ /// Gets the RTS forward GUID.
+ /// </summary>
+ public static string ForwardGUID { get; internal set; }
+
+ /// <summary>
+ /// Occurs when the user inputs text in a Terminal.
+ /// </summary>
+ public static event TextSentEventHandler TextSent;
+
+ /// <summary>
+ /// Fakes the user inputting text to a Terminal.
+ /// </summary>
+ /// <param name="text">The text to input.</param>
+ public static void SendText(string text)
+ {
+ TextSent?.Invoke(text);
+ }
+
+ }
+}
diff --git a/ShiftOS_TheReturn/TerminalTextWriter.cs b/ShiftOS_TheReturn/TerminalTextWriter.cs
new file mode 100644
index 0000000..010e02c
--- /dev/null
+++ b/ShiftOS_TheReturn/TerminalTextWriter.cs
@@ -0,0 +1,160 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ShiftOS.Engine
+{
+ /// <summary>
+ /// Backend class for forwarding <see cref="System.Console"/> to the ShiftOS terminal.
+ /// </summary>
+ public class TerminalTextWriter : TextWriter
+ {
+ /// <summary>
+ /// Win32 API call to lock the window from being updated.
+ /// </summary>
+ /// <param name="hWndLock">The Win32 window handle</param>
+ /// <returns>...I....have no idea.</returns>
+ [System.Runtime.InteropServices.DllImport("user32.dll")]
+ public static extern bool LockWindowUpdate(IntPtr hWndLock);
+
+ /// <summary>
+ /// Gets the encoding format for this <see cref="TextWriter"/>. God bless the Unicode Consortiem.
+ /// </summary>
+ public override Encoding Encoding
+ {
+ get
+ {
+ return Encoding.Unicode;
+ }
+ }
+
+ /// <summary>
+ /// Gets the underlying <see cref="ITerminalWidget"/> that this <see cref="TerminalTextWriter"/> is forwarding to.
+ /// </summary>
+ public ITerminalWidget UnderlyingControl
+ {
+ get
+ {
+ return AppearanceManager.ConsoleOut;
+ }
+ }
+
+ /// <summary>
+ /// Moves the caret to the last character in the textbox.
+ /// </summary>
+ public void select()
+ {
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ UnderlyingControl?.SelectBottom();
+
+ }));
+ }
+
+ /// <summary>
+ /// Write a character to the Terminal.
+ /// </summary>
+ /// <param name="value">The character to write.</param>
+ public override void Write(char value)
+ {
+ if (TerminalBackend.IsForwardingConsoleWrites)
+ {
+ ServerManager.SendMessage("write", $@"{{
+ guid: ""{TerminalBackend.ForwardGUID}"",
+ text: ""{value}""
+}}");
+ }
+ else
+ {
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ UnderlyingControl?.Write(value.ToString());
+ select();
+ }));
+ }
+ }
+
+ /// <summary>
+ /// Write text to the Terminal, followed by a newline.
+ /// </summary>
+ /// <param name="value">The text to write.</param>
+ public override void WriteLine(string value)
+ {
+ if (TerminalBackend.IsForwardingConsoleWrites)
+ {
+ ServerManager.SendMessage("write", $@"{{
+ guid: ""{TerminalBackend.ForwardGUID}"",
+ text: ""{value + Environment.NewLine}""
+}}");
+ }
+ else
+ {
+
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ UnderlyingControl?.WriteLine(value);
+ select();
+ }));
+ }
+ }
+
+ [Obsolete("Stub.")]
+ public void SetLastText()
+ {
+ }
+
+ /// <summary>
+ /// Write text to the Terminal.
+ /// </summary>
+ /// <param name="value">The text to write.</param>
+ public override void Write(string value)
+ {
+ if (TerminalBackend.IsForwardingConsoleWrites)
+ {
+ ServerManager.SendMessage("write", $@"{{
+ guid: ""{TerminalBackend.ForwardGUID}"",
+ text: ""{value}""
+}}");
+ }
+ else
+ {
+
+ Desktop.InvokeOnWorkerThread(new Action(() =>
+ {
+ UnderlyingControl?.Write(value.ToString());
+ select();
+ }));
+ }
+ }
+
+
+ }
+}
diff --git a/ShiftOS_TheReturn/TutorialManager.cs b/ShiftOS_TheReturn/TutorialManager.cs
index ea78cd8..13df153 100644
--- a/ShiftOS_TheReturn/TutorialManager.cs
+++ b/ShiftOS_TheReturn/TutorialManager.cs
@@ -30,10 +30,18 @@ using System.Threading.Tasks;
namespace ShiftOS.Engine
{
+ [Obsolete("This isn't used... I don't think...")]
public static class TutorialManager
{
+ /// <summary>
+ /// The tutorial frontend.
+ /// </summary>
private static ITutorial _tut = null;
+ /// <summary>
+ /// Registers a tutorial frontend to the backend.
+ /// </summary>
+ /// <param name="tut"></param>
public static void RegisterTutorial(ITutorial tut)
{
IsInTutorial = false;
diff --git a/ShiftOS_TheReturn/UniteClient.cs b/ShiftOS_TheReturn/UniteClient.cs
new file mode 100644
index 0000000..d8e34b7
--- /dev/null
+++ b/ShiftOS_TheReturn/UniteClient.cs
@@ -0,0 +1,236 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using ShiftOS.Objects;
+
+namespace ShiftOS.Unite
+{
+ public class UniteClient
+ {
+ /// <summary>
+ /// Gets a string represents the user token for this Unite Client.
+ /// </summary>
+ public string Token { get; private set; }
+
+ /// <summary>
+ /// Gets the base URL used in all API calls. Retrieved from the user's servers.json file.
+ /// </summary>
+ public string BaseURL
+ {
+ get
+ {
+ return UserConfig.Get().UniteUrl;
+ }
+ }
+
+ /// <summary>
+ /// Get the display name of a user.
+ /// </summary>
+ /// <param name="id">The user ID to look at.</param>
+ /// <returns></returns>
+ public string GetDisplayNameId(string id)
+ {
+ return MakeCall("/API/GetDisplayName/" + id);
+ }
+
+ /// <summary>
+ /// Get the Pong highscore stats for all users.
+ /// </summary>
+ /// <returns></returns>
+ public PongHighscoreModel GetPongHighscores()
+ {
+ return JsonConvert.DeserializeObject<PongHighscoreModel>(MakeCall("/API/GetPongHighscores"));
+ }
+
+ /// <summary>
+ /// Create a new instance of the <see cref="UniteClient"/> object.
+ /// </summary>
+ /// <param name="baseurl">Unused.</param>
+ /// <param name="usertoken">The user API token to use for this client (see http://getshiftos.ml/Manage and click "API" to see your tokens)</param>
+ public UniteClient(string baseurl, string usertoken)
+ {
+ //Handled by the servers.json file
+ //BaseURL = baseurl;
+ Token = usertoken;
+ }
+
+ /// <summary>
+ /// Make a call to the Unite API using the current user token and base URL.
+ /// </summary>
+ /// <param name="url">The path, relative to the base URL, to call.</param>
+ /// <returns>The server's response.</returns>
+ internal string MakeCall(string url)
+ {
+ var webrequest = WebRequest.Create(BaseURL + url);
+ webrequest.Headers.Add("Authentication: Token " + Token);
+ using (var response = webrequest.GetResponse())
+ {
+ using (var stream = response.GetResponseStream())
+ {
+ using (var reader = new System.IO.StreamReader(stream))
+ {
+ return reader.ReadToEnd();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Get the Pong codepoint highscore for the current user.
+ /// </summary>
+ /// <returns>The amount of Codepoints returned by the server</returns>
+ public int GetPongCP()
+ {
+ return Convert.ToInt32(MakeCall("/API/GetPongCP"));
+ }
+
+ /// <summary>
+ /// Get the pong highest level score for this user
+ /// </summary>
+ /// <returns>The highest level the user has reached.</returns>
+ public int GetPongLevel()
+ {
+ return Convert.ToInt32(MakeCall("/API/GetPongLevel"));
+ }
+
+ /// <summary>
+ /// Set the user's highest level record for Pong.
+ /// </summary>
+ /// <param name="value">The level to set the record to.</param>
+ public void SetPongLevel(int value)
+ {
+ MakeCall("/API/SetPongLevel/" + value.ToString());
+ }
+
+ /// <summary>
+ /// Set the pong Codepoints record for the user
+ /// </summary>
+ /// <param name="value">The amount of Codepoints to set the record to</param>
+ public void SetPongCP(int value)
+ {
+ MakeCall("/API/SetPongCP/" + value.ToString());
+ }
+
+ /// <summary>
+ /// Get the user's email address.
+ /// </summary>
+ /// <returns>The user's email address.</returns>
+ public string GetEmail()
+ {
+ return MakeCall("/API/GetEmail");
+ }
+
+ /// <summary>
+ /// Get the user's system name.
+ /// </summary>
+ /// <returns>The user's system name.</returns>
+ public string GetSysName()
+ {
+ return MakeCall("/API/GetSysName");
+ }
+
+ /// <summary>
+ /// Set the user's system name.
+ /// </summary>
+ /// <param name="value">The system name to set the record to.</param>
+ public void SetSysName(string value)
+ {
+ MakeCall("/API/SetSysName/" + value);
+ }
+
+ /// <summary>
+ /// Get the user's display name.
+ /// </summary>
+ /// <returns>The user's display name.</returns>
+ public string GetDisplayName()
+ {
+ return MakeCall("/API/GetDisplayName");
+ }
+
+ /// <summary>
+ /// Set the user's display name.
+ /// </summary>
+ /// <param name="value">The display name to set the user's account to.</param>
+ public void SetDisplayName(string value)
+ {
+ MakeCall("/API/SetDisplayName/" + value.ToString());
+ }
+
+ /// <summary>
+ /// Get the user's full name if they have set it in their profile.
+ /// </summary>
+ /// <returns>Empty string if the user hasn't set their fullname, else, a string representing their fullname.</returns>
+ public string GetFullName()
+ {
+ return MakeCall("/API/GetFullName");
+ }
+
+ /// <summary>
+ /// Set the user's fullname.
+ /// </summary>
+ /// <param name="value">The new fullname.</param>
+ public void SetFullName(string value)
+ {
+ MakeCall("/API/SetFullName/" + value.ToString());
+ }
+
+ /// <summary>
+ /// Get the user's codepoints.
+ /// </summary>
+ /// <returns>The amount of codepoints stored on the server for this user.</returns>
+ public long GetCodepoints()
+ {
+ return Convert.ToInt64(MakeCall("/API/GetCodepoints"));
+ }
+
+ /// <summary>
+ /// Set the user's codepoints.
+ /// </summary>
+ /// <param name="value">The amount of codepoints to set the user's codepoints value to.</param>
+ public void SetCodepoints(long value)
+ {
+ MakeCall("/API/SetCodepoints/" + value.ToString());
+ }
+ }
+
+ /// <summary>
+ /// API data model for Unite pong highscores.
+ /// </summary>
+ public class PongHighscoreModel
+ {
+ /// <summary>
+ /// Amount of pages in this list.
+ /// </summary>
+ public int Pages { get; set; }
+
+ /// <summary>
+ /// An array representing the highscores found on the server.
+ /// </summary>
+ public PongHighscore[] Highscores { get; set; }
+ }
+
+ /// <summary>
+ /// API data model for a single Pong highscore.
+ /// </summary>
+ public class PongHighscore
+ {
+ /// <summary>
+ /// The user ID linked to this highscore.
+ /// </summary>
+ public string UserId { get; set; }
+
+ /// <summary>
+ /// The highscore's level record.
+ /// </summary>
+ public int Level { get; set; }
+
+ /// <summary>
+ /// The highscore's codepoint cashout record.
+ /// </summary>
+ public long CodepointsCashout { get; set; }
+ }
+}
diff --git a/ShiftOS_TheReturn/UniteTestCommands.cs b/ShiftOS_TheReturn/UniteTestCommands.cs
deleted file mode 100644
index 7a83e44..0000000
--- a/ShiftOS_TheReturn/UniteTestCommands.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace ShiftOS.Engine
-{
- [Namespace("unite")]
- public static class UniteTestCommands
- {
- [Command("setdisplayname")]
- [RequiresArgument("name")]
- public static bool SetDisplayName(Dictionary<string, object> args)
- {
- string dname = args["name"].ToString();
- var unite = new ShiftOS.Unite.UniteClient("http://getshiftos.ml", SaveSystem.CurrentSave.UniteAuthToken);
- unite.SetDisplayName(dname);
- return true;
- }
-
- [Command("login")]
- [RequiresArgument("username")]
- [RequiresArgument("password")]
- public static bool LoginTest(Dictionary<string, object> args)
- {
- string u = args["username"].ToString();
- string p = args["password"].ToString();
- var webrequest = HttpWebRequest.Create("http://getshiftos.ml/Auth/Login?appname=ShiftOS&appdesc=ShiftOS+client&version=1_0_beta_2_4");
- string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{u}:{p}"));
- webrequest.Headers.Add("Authentication: Basic " + base64);
- var response = webrequest.GetResponse();
- var str = response.GetResponseStream();
- var reader = new System.IO.StreamReader(str);
- string result = reader.ReadToEnd();
- Console.WriteLine("Server returned: " + result);
- reader.Close();
- str.Close();
- str.Dispose();
- response.Dispose();
-
- return true;
- }
-
- [Command("linklogin")]
- [RequiresArgument("username")]
- [RequiresArgument("password")]
- public static bool LinkLogin(Dictionary<string, object> args)
- {
- string u = args["username"].ToString();
- string p = args["password"].ToString();
- var webrequest = HttpWebRequest.Create("http://getshiftos.ml/Auth/Login?appname=ShiftOS&appdesc=ShiftOS+client&version=1_0_beta_2_4");
- string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{u}:{p}"));
- webrequest.Headers.Add("Authentication: Basic " + base64);
- var response = webrequest.GetResponse();
- var str = response.GetResponseStream();
- var reader = new System.IO.StreamReader(str);
- string result = reader.ReadToEnd();
- //If we get this far, the token is OURS. :D
- SaveSystem.CurrentSave.UniteAuthToken = result;
- Console.WriteLine("Unite says \"Access Granted!\"");
- SaveSystem.SaveGame();
- reader.Close();
- str.Close();
- str.Dispose();
- response.Dispose();
-
- return true;
- }
-
-
- }
-}
diff --git a/ShiftOS_TheReturn/VirusEngine.cs b/ShiftOS_TheReturn/VirusEngine.cs
deleted file mode 100644
index 650db92..0000000
--- a/ShiftOS_TheReturn/VirusEngine.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Newtonsoft.Json;
-using static ShiftOS.Objects.ShiftFS.Utils;
-
-
-namespace ShiftOS.Engine
-{
- public static class VirusEngine
- {
- public static void InfectFile(string file, string virusid)
- {
- var infected = new List<string>();
- var hData = GetHeaderText(file);
-
- if (hData == "")
- {
- infected.Add(virusid);
- }
- else
- {
- infected = JsonConvert.DeserializeObject<List<string>>(hData);
- if (!infected.Contains(virusid))
- infected.Add(virusid);
- }
-
- SetHeaderText(file, JsonConvert.SerializeObject(infected));
- }
-
- public static void DisinfectFile(string file, int threatlevel)
- {
- var infected = new List<string>();
- var hData = GetHeaderText(file);
-
- if (hData != "")
- {
- infected = JsonConvert.DeserializeObject<List<string>>(hData);
- for (int i = 0; i < infected.Count; i++)
- {
- string[] splitID = infected[i].Split('.');
- int th = Convert.ToInt32(splitID[splitID.Length - 1]);
- if (th <= threatlevel)
- {
- infected.RemoveAt(i);
- }
- }
- }
-
- SetHeaderText(file, JsonConvert.SerializeObject(infected));
- }
-
- internal static string[] FindAllVirusesInFile(string file)
- {
- string hdata = GetHeaderText(file);
- return (hdata != "") ? new string[0] : JsonConvert.DeserializeObject<string[]>(hdata);
- }
-
- }
-
- public abstract class Virus
- {
- /// <summary>
- /// Inject the virus into system memory by running it.
- /// </summary>
- public abstract void Activate();
-
- /// <summary>
- /// Terminate the virus.
- /// </summary>
- public abstract void Deactivate();
-
- public abstract int ThreatLevel { get; }
-
- public abstract string Signature { get; }
-
- public abstract string Type { get; }
- }
-}