aboutsummaryrefslogtreecommitdiff
path: root/ShiftOS/ShiftOS/SystemContext.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ShiftOS/ShiftOS/SystemContext.cs')
-rw-r--r--ShiftOS/ShiftOS/SystemContext.cs449
1 files changed, 443 insertions, 6 deletions
diff --git a/ShiftOS/ShiftOS/SystemContext.cs b/ShiftOS/ShiftOS/SystemContext.cs
index 4409282..95da1d5 100644
--- a/ShiftOS/ShiftOS/SystemContext.cs
+++ b/ShiftOS/ShiftOS/SystemContext.cs
@@ -6,12 +6,21 @@ using System.Windows.Forms;
using ShiftOS.Metadata;
using ShiftOS.Windowing;
using System.Reflection;
+using Newtonsoft.Json;
+using ShiftOS.Commands;
using System.Threading.Tasks;
+using System.Drawing;
+using System.IO;
+using System.Security.Cryptography;
namespace ShiftOS
{
public class SystemContext : IDisposable
{
+ private readonly byte[] SAVE_MAGIC = Encoding.UTF8.GetBytes("7R3Y");
+
+ private string _username = "user";
+ private string _osname = "shiftos";
private Desktop _desktop = null;
private SkinContext _skinContext = null;
private FilesystemContext _filesystem = null;
@@ -19,16 +28,358 @@ namespace ShiftOS
private List<Window> _windows = new List<Window>();
private Dictionary<string, Type> _programTypeMap = new Dictionary<string, Type>();
private List<ProgramAttribute> _programMetadata = new List<ProgramAttribute>();
+ private List<Upgrade> _upgrades = new List<Upgrade>();
+ private List<string> _installedUpgrades = new List<string>();
+ public event EventHandler DesktopUpdated;
+ private List<TerminalCommand> _commands = new List<TerminalCommand>();
+ public Bitmap FindBitmapResource(string id)
+ {
+ var type = typeof(Properties.Resources);
- public event EventHandler DesktopUpdated;
+ var property = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static).FirstOrDefault(x => x.Name == id && x.PropertyType == typeof(Bitmap));
- public bool HasShiftoriumUpgrade(string InUpgrade)
+ if (property == null)
+ return null;
+
+ return property.GetValue(null) as Bitmap;
+ }
+
+ private void LoadSaveFile()
+ {
+ Console.WriteLine("Loading save file...");
+ if(File.Exists("shiftos.sav"))
+ {
+ Console.WriteLine(" --> Checking file type...");
+ using (var stream = File.OpenRead("shiftos.sav"))
+ {
+ using (var reader = new BinaryReader(stream, Encoding.UTF8))
+ {
+ byte[] magic = reader.ReadBytes(SAVE_MAGIC.Length);
+
+ if (!magic.SequenceEqual(SAVE_MAGIC))
+ {
+ MessageBox.Show($"Your ShiftOS save file is incompatible with this version of ShiftOS. We cannot load it.{Environment.NewLine}{Environment.NewLine}The game will ignore the existence of your save file.", "Invalid or incompatible save file", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ int hashLengthBytes = reader.ReadInt32();
+ byte[] hash = reader.ReadBytes(hashLengthBytes);
+
+ int dataLengthBytes = reader.ReadInt32();
+ byte[] data = reader.ReadBytes(dataLengthBytes);
+
+ using (SHA256 sha = SHA256.Create())
+ {
+ if (!sha.ComputeHash(data).SequenceEqual(hash))
+ {
+ MessageBox.Show("Your save file has been tampered with. Trying to cheat, fucker? Okay, cool. We just won't load your save. Bitch.", "Ass-tastic attempt at cheating there, brah.", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ using (var dataStream = new MemoryStream(data))
+ {
+ using (var dataReader = new BinaryReader(dataStream, Encoding.UTF8))
+ {
+ _codepoints = dataReader.ReadInt32();
+ _installedUpgrades = new List<string>();
+ int upgradeCount = dataReader.ReadInt32();
+ for (int i = 0; i < upgradeCount; i++)
+ {
+ _installedUpgrades.Add(dataReader.ReadString());
+ }
+ _username = dataReader.ReadString();
+ _osname = dataReader.ReadString();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void SaveGame()
+ {
+ using (var stream = File.Open("shiftos.sav", FileMode.OpenOrCreate))
+ {
+ using (var writer = new BinaryWriter(stream, Encoding.UTF8))
+ {
+ writer.Write(SAVE_MAGIC);
+
+ byte[] hash = null;
+ byte[] data = null;
+
+ using (var dataStream = new MemoryStream())
+ {
+ using (var dataWriter = new BinaryWriter(dataStream, Encoding.UTF8, true))
+ {
+ dataWriter.Write(_codepoints);
+ dataWriter.Write(_installedUpgrades.Count);
+ foreach(var upgrade in _installedUpgrades)
+ {
+ dataWriter.Write(upgrade);
+ }
+ dataWriter.Write(_username);
+ dataWriter.Write(_osname);
+ }
+
+ data = dataStream.ToArray();
+ }
+
+ using (var sha = SHA256.Create())
+ {
+ hash = sha.ComputeHash(data);
+ }
+
+ writer.Write(hash.Length);
+ writer.Write(hash);
+ writer.Write(data.Length);
+ writer.Write(data);
+ }
+ }
+ }
+
+ public string Username { get => _username; set => _username = value; }
+ public string OSName { get => _osname; set => _osname = value; }
+
+ private void LoadTerminalCommands()
+ {
+ Console.WriteLine("Loading Terminal Commands...");
+
+ var ass = Assembly.GetExecutingAssembly();
+
+ var types = ass.GetTypes().Where(x => x.BaseType == typeof(TerminalCommand));
+
+ foreach(var type in types)
+ {
+ var command = Activator.CreateInstance(type, null) as TerminalCommand;
+
+ if(_commands.Any(x=>x.Name == command.Name))
+ {
+ Console.WriteLine(" --> WARNING: Duplicate command: {0}", command.Name);
+ continue;
+ }
+
+ Console.WriteLine(" --> Loaded {0} ({1})", command.Name, type.FullName);
+ _commands.Add(command);
+ }
+ }
+
+ private string[] Tokenize(string InCommand, ref string OutputError)
{
- // TODO: Shiftorium.
+ // Port of some Peacenet UE4 code - doesn't use anything specific to Unreal so...
+ List<string> tokens = new List<string>();
+ string current = "";
+ bool escaping = false;
+ bool inQuote = false;
+
+ int cmdLength = InCommand.Length;
+
+ char[] cmd = InCommand.ToCharArray();
+
+ for (int i = 0; i < cmdLength; i++)
+ {
+ char c = cmd[i];
+ if (c == '\\')
+ {
+ if (escaping == false)
+ escaping = true;
+ else
+ {
+ escaping = false;
+ current += c;
+ }
+ continue;
+ }
+ if (escaping == true)
+ {
+ switch (c)
+ {
+ case ' ':
+ current+=' ';
+ break;
+ case 'n':
+ current+='\n';
+ break;
+ case 'r':
+ current+='\r';
+ break;
+ case 't':
+ current+='\t';
+ break;
+ case '"':
+ current+='"';
+ break;
+ default:
+ OutputError = "unrecognized escape sequence.";
+ return new string[0];
+ }
+ escaping = false;
+ continue;
+ }
+ if (c == ' ')
+ {
+ if (inQuote)
+ {
+ current+=c;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(current))
+ {
+ tokens.Add(current);
+ current = "";
+ }
+ }
+ continue;
+ }
+ if (c == '"')
+ {
+ inQuote = !inQuote;
+ if (!inQuote)
+ {
+ if (i + 1 < cmdLength)
+ {
+ if (cmd[i + 1] == '"')
+ {
+ OutputError = "String splice detected. Did you mean to use a literal double-quote (\\\")?";
+ return new string[0];
+ }
+ }
+ }
+ continue;
+ }
+ current+=c;
+ }
+ if (inQuote)
+ {
+ OutputError = "expected ending double-quote, got end of command instead.";
+ return new string[0];
+ }
+ if (escaping)
+ {
+ OutputError = "expected escape sequence, got end of command instead.";
+ return new string[0];
+ }
+ if (!string.IsNullOrEmpty(current))
+ {
+ tokens.Add(current);
+ current = "";
+ }
+ return tokens.ToArray();
+
+ }
+
+ public void ExecuteCommand(IConsoleContext InConsole, string InCommand)
+ {
+ if (string.IsNullOrWhiteSpace(InCommand))
+ return;
+
+ string err = "";
+ var tokens = Tokenize(InCommand, ref err);
+ if(!string.IsNullOrWhiteSpace(err))
+ {
+ InConsole.WriteLine(err);
+ return;
+ }
+
+ var installedCommand = GetInstalledCommands().FirstOrDefault(x => x.Name == tokens[0]);
+
+ if(installedCommand == null)
+ {
+ InConsole.WriteLine("Command not found - type \"help\" for a list of commands.");
+ return;
+ }
+
+ var docoptUsage = "Usage: " + installedCommand.Usage;
+
+ var docoptArgv = tokens.Skip(1).ToArray();
+
+ var docopt = new DocoptNet.Docopt();
+
+ try
+ {
+ var docoptValues = docopt.Apply(docoptUsage, docoptArgv, false, null, true, false);
+
+ Dictionary<string, object> realValues = new Dictionary<string, object>();
+
+ foreach(var kvs in docoptValues)
+ {
+ realValues.Add(kvs.Key, kvs.Value.Value);
+ }
+
+ installedCommand.Run(InConsole, realValues);
+ }
+ catch(Exception ex)
+ {
+ InConsole.WriteLine(ex.Message);
+ return;
+ }
+
+ }
+
+ public IEnumerable<TerminalCommand> GetInstalledCommands()
+ {
+ foreach (var command in _commands)
+ {
+ var type = command.GetType();
+ var requirements = type.GetCustomAttributes(true).Where(x => x is RequiresAttribute);
+
+ if (requirements.Count() == 0)
+ {
+ yield return command;
+ continue;
+ }
+ if (requirements.Any(x => !(x as RequiresAttribute).IsFulfilled(this)))
+ continue;
+ yield return command;
+ }
+ }
+
+ public void AddCodepoints(int InCodepoints)
+ {
+ _codepoints += Math.Abs(InCodepoints);
+ }
+
+ public IEnumerable<Upgrade> GetUpgrades()
+ {
+ return _upgrades.Where(x => x.IsAvailable(this));
+ }
+
+ public bool Buy(Upgrade InUpgrade)
+ {
+ if (!InUpgrade.CanBuy(this))
+ {
+ return false;
+ }
+ _codepoints -= InUpgrade.Cost;
+ _installedUpgrades.Add(InUpgrade.ID);
+ Console.WriteLine(" --> Bought {0}", InUpgrade.Name);
+ _desktop.ResetAppLauncher();
+ this.SaveGame();
return true;
}
+ private void LoadUpgrades()
+ {
+ Console.WriteLine("Loading Shiftorium upgrade database...");
+ Console.WriteLine(" --> Loading internal resource DB...");
+
+ _upgrades = JsonConvert.DeserializeObject<List<Upgrade>>(Properties.Resources.UpgradeDatabase);
+ Console.WriteLine(" --> {0} upgrades loaded. Finalizing upgrade data...", _upgrades.Count);
+
+ foreach(var upgrade in _upgrades)
+ {
+ Console.WriteLine(" --> Finalizing {0} ({1})...", upgrade.Name, upgrade.ID);
+ upgrade.FinalizeUpgrade();
+ }
+
+ }
+
+ public bool HasShiftoriumUpgrade(string InUpgrade)
+ {
+ return _installedUpgrades.Contains(InUpgrade);
+ }
+
public bool LaunchProgram(string InExecutableName)
{
// Does the program exist in our typemap?
@@ -40,7 +391,22 @@ namespace ShiftOS
Type ProgramType = this._programTypeMap[InExecutableName];
- // TODO: Check if the program is installed...
+ var requirements = ProgramType.GetCustomAttributes(true).Where(x => x is RequiresAttribute);
+
+ if (requirements.Count() > 0)
+ {
+ if (requirements.Any(x => !(x as RequiresAttribute).IsFulfilled(this)))
+ return false;
+ }
+
+ // If we don't have multitasking, we need to close all existing windows.
+ if(!HasShiftoriumUpgrade("multitasking"))
+ {
+ while(_windows.Count>0)
+ {
+ _windows[0].Close();
+ }
+ }
// Create the program window.
var window = (Window)Activator.CreateInstance(ProgramType, null);
@@ -83,6 +449,21 @@ namespace ShiftOS
return this._programMetadata.FirstOrDefault(x => x.ExecutableName == InExecutableName)?.FriendlyName;
}
+ public bool IsAppLauncherItemAvailable(string InExecutableName)
+ {
+ if (!_programTypeMap.ContainsKey(InExecutableName))
+ return false;
+
+ var type = _programTypeMap[InExecutableName];
+
+ var attributes = type.GetCustomAttributes(true).Where(x => x is AppLauncherRequirementAttribute);
+
+ if (attributes.Count() == 0)
+ return true;
+
+ return !attributes.Any(x => !(x as AppLauncherRequirementAttribute).IsFulfilled(this));
+ }
+
private void LoadProgramData()
{
Console.WriteLine("Loading program metadata...");
@@ -104,6 +485,8 @@ namespace ShiftOS
}
}
}
+
+ LoadTerminalCommands();
}
private void LoadCurrentSkin()
@@ -155,6 +538,11 @@ namespace ShiftOS
return this._filesystem;
}
+ public void Shutdown()
+ {
+ _desktop.Close();
+ }
+
public SkinContext GetSkinContext()
{
return this._skinContext;
@@ -171,12 +559,19 @@ namespace ShiftOS
// Set up WinForms to run normally.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
+
ToolStripManager.Renderer = new ToolStripSkinRenderer(this);
+ this.LoadSaveFile();
+
+ this.LoadUpgrades();
+
// Load all programs in the game.
this.LoadProgramData();
+
+
// Load up the filesystem.
this.LoadFilesystem();
@@ -189,12 +584,54 @@ namespace ShiftOS
// Run Windows Forms.
Application.Run(_desktop);
}
+
+ this.SaveGame();
}
public string GetTimeOfDay()
{
- // TODO: Shiftorium time upgrades.
- return DateTime.Now.ToShortTimeString();
+ // Get the current date/time.
+ var now = DateTime.Now;
+
+ if(HasShiftoriumUpgrade("splitsecondtime"))
+ {
+ return now.ToLongTimeString();
+ }
+ else
+ {
+ if(HasShiftoriumUpgrade("minuteaccuracytime"))
+ {
+ return now.ToShortTimeString();
+ }
+ else
+ {
+ if (HasShiftoriumUpgrade("hourssincemidnight"))
+ {
+ int hour = (int)now.TimeOfDay.TotalHours;
+
+ if(HasShiftoriumUpgrade("pmandam"))
+ {
+ string am = "AM";
+ if(hour > 12)
+ {
+ am = "PM";
+ hour = hour - 12;
+ }
+ return $"{hour} {am}";
+ }
+
+ return hour.ToString();
+ }
+ else if(HasShiftoriumUpgrade("minutessincemidnight"))
+ {
+ return ((int)now.TimeOfDay.TotalMinutes).ToString();
+ }
+ else
+ {
+ return ((int)now.TimeOfDay.TotalSeconds).ToString();
+ }
+ }
+ }
}
}
}