From 9df7aa2110dc037a6922c5d6e4c0534fcf18b6ef Mon Sep 17 00:00:00 2001 From: RogueAI42 Date: Fri, 16 Jun 2017 18:34:30 +1000 Subject: Lunix penguins = good --- LinuxLauncher/README.md | 24 +++++++++++++ LinuxLauncher/shiftos.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 LinuxLauncher/README.md create mode 100644 LinuxLauncher/shiftos.py (limited to 'LinuxLauncher') diff --git a/LinuxLauncher/README.md b/LinuxLauncher/README.md new file mode 100644 index 0000000..32f3b5a --- /dev/null +++ b/LinuxLauncher/README.md @@ -0,0 +1,24 @@ +This is an attempt to get ShiftOS working well on Linux using Wine and +Mono. + +The launcher script needs a copy of the game (try extracting Debug.zip +from the AppVeyor project) in a directory called "data". It will set up +a new Wine prefix in ~/.local/share/ShiftOS and the game itself can be +found in ~/.local/share/ShiftOS/drive_c/ShiftOS. Ultimately I want to +make an AUR package of this script if all goes to plan. + +## known bugs + +* the first time you start the game, the Wine virtual desktop doesn't +enable properly +* the story mode intro crashes when it starts to "format" your drive +* the ShiftOS desktop can go in front of applications +* there is a blue border from the Wine desktop (I want to change that +to black, but I also don't want it showing through while the game is +running) +* CSharpCodeProvider doesn't seem to work, so Python mods won't load + +## anticipated tricky bits + +* Web browser +* Unity mode diff --git a/LinuxLauncher/shiftos.py b/LinuxLauncher/shiftos.py new file mode 100644 index 0000000..7fa5a6d --- /dev/null +++ b/LinuxLauncher/shiftos.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +import os, xdg.BaseDirectory, sys, subprocess, tempfile, distutils.spawn + +def GetPrimaryDisplayCurrentResolution(): + xrandr = subprocess.check_output(["xrandr"]) + if not isinstance(xrandr, str): + xrandr = xrandr.decode() + elems = xrandr.splitlines() + item = "" + res = None + while not item.startswith(" "): + item = elems.pop() + while item.startswith(" "): + if "*" in item: + res = item.split(" ")[1] + break + item = elems.pop() + if not res: + raise OSError("Failed to find the screen resolution.") + return res + +def RunCmd(cmd): + if os.system(cmd) != 0: + raise OSError(cmd) + +def SetRegistryKeys(dictionary): + with tempfile.NamedTemporaryFile(suffix = ".reg") as reg: + regdata = "Windows Registry Editor Version 5.00\r\n" + for key, val in dictionary.items(): + seg = key.split("\\") + path = "\\".join(seg[:-1]) + realkey = seg[-1] + regdata += '\r\n[{0}]\r\n"{1}"="{2}"\r\n'.format(path, realkey, val) + reg.write(regdata.encode()) + reg.flush() + RunCmd("{0} regedit /C {1}".format(WinePath, reg.name)) + +def UpdateSymlinks(src, dest): + src = os.path.abspath(src) + dest = os.path.abspath(dest) + + # Add new directories and symlinks to the destination folder. + for subdir, dirs, files in os.walk(src): + for dirname in dirs: + destpath = os.path.join(dest, os.path.relpath(subdir, src), dirname) + if not os.path.isdir(destpath): + os.makedirs(destpath) + for fname in files: + srcpath = os.path.join(subdir, fname) + destpath = os.path.join(dest, os.path.relpath(subdir, src), fname) + if not os.path.exists(destpath): + os.symlink(srcpath, destpath) + + # Prune old symlinks from the destination folder. + for subdir, dirs, files in os.walk(dest): + for fname in files: + srcpath = os.path.join(subdir, fname) + destpath = os.path.join(dest, os.path.relpath(subdir, src), fname) + if os.path.islink(destpath): + if os.readlink(destpath).startswith(src): + if not os.path.exists(srcpath): + os.remove(destpath) + +GamePath = os.path.dirname(os.path.realpath(__file__)) + +GlobalDataPath = os.path.join(GamePath, "data") + +WinePath = distutils.spawn.find_executable("wine64") +if not WinePath: + WinePath = distutils.spawn.find_executable("wine") +if not WinePath: + raise FileNotFoundError("Could not find 'wine64' or 'wine'.") + +HomePath = os.path.join(xdg.BaseDirectory.xdg_data_home, "ShiftOS") +LocalDataPath = os.path.join(HomePath, "drive_c", "ShiftOS") + +if __name__ == "__main__": + + os.environ["WINEPREFIX"] = HomePath + + if not os.path.exists(HomePath): + RunCmd("wineboot") + + UpdateSymlinks(GlobalDataPath, LocalDataPath) + + os.chdir(LocalDataPath) + + SetRegistryKeys( + { + r"HKEY_CURRENT_USER\Software\Wine\Explorer\Desktop": "Default", + r"HKEY_CURRENT_USER\Software\Wine\Explorer\Desktops\Default": GetPrimaryDisplayCurrentResolution() + }) + + RunCmd("{0} ShiftOS.WinForms.exe".format(WinePath)) -- cgit v1.2.3 From 50bd1ab402918ede7e4e365ca91b2300ebc20741 Mon Sep 17 00:00:00 2001 From: RogueAI42 Date: Fri, 16 Jun 2017 23:20:10 +1000 Subject: Fix Intro on Linux MORE LUNIX, MORE FUN also replaced drive type with drive format --- LinuxLauncher/README.md | 3 +- ShiftOS.WinForms/OobeStory.cs | 56 +++++---- ShiftOS_TheReturn/Lunix.cs | 201 ++++++++++++++++++++++++++++++++ ShiftOS_TheReturn/ShiftOS.Engine.csproj | 1 + 4 files changed, 238 insertions(+), 23 deletions(-) create mode 100644 ShiftOS_TheReturn/Lunix.cs (limited to 'LinuxLauncher') diff --git a/LinuxLauncher/README.md b/LinuxLauncher/README.md index 32f3b5a..7ee2a74 100644 --- a/LinuxLauncher/README.md +++ b/LinuxLauncher/README.md @@ -11,7 +11,8 @@ make an AUR package of this script if all goes to plan. * the first time you start the game, the Wine virtual desktop doesn't enable properly -* the story mode intro crashes when it starts to "format" your drive +* text input boxes are white on white +* the terminal puts an extra newline after displaying the prompt * the ShiftOS desktop can go in front of applications * there is a blue border from the Wine desktop (I want to change that to black, but I also don't want it showing through while the game is diff --git a/ShiftOS.WinForms/OobeStory.cs b/ShiftOS.WinForms/OobeStory.cs index f2a4930..cab1ec8 100644 --- a/ShiftOS.WinForms/OobeStory.cs +++ b/ShiftOS.WinForms/OobeStory.cs @@ -85,28 +85,40 @@ namespace ShiftOS.WinForms ConsoleEx.ForegroundColor = ConsoleColor.White; Console.WriteLine(@"We'll now begin formatting your drive. Please be patient."); Console.WriteLine(); - var dinf = new DriveInfo("C:\\"); - decimal bytesFree = ((dinf.AvailableFreeSpace / 1024) / 1024) / 1024; - decimal totalBytes = ((dinf.TotalSize / 1024) / 1024) / 1024; - string type = dinf.DriveType.ToString(); - string name = dinf.Name; - ConsoleEx.Bold = true; - Console.Write("Drive name: "); - ConsoleEx.Bold = false; - Console.WriteLine(name); - ConsoleEx.Bold = true; - Console.Write("Drive type: "); - ConsoleEx.Bold = false; - Console.WriteLine(type); - ConsoleEx.Bold = true; - Console.Write("Total space: "); - ConsoleEx.Bold = false; - Console.WriteLine(totalBytes.ToString() + " GB"); - ConsoleEx.Bold = true; - Console.Write("Free space: "); - Console.WriteLine(bytesFree.ToString() + " GB"); - Console.WriteLine(); - + double bytesFree, totalBytes; + string type, name; + dynamic dinf; + try + { + if (Lunix.InWine) + dinf = new Lunix.DFDriveInfo("/"); + else + dinf = new DriveInfo("C:\\"); + bytesFree = dinf.AvailableFreeSpace / 1073741824.0; + totalBytes = dinf.TotalSize / 1073741824.0; + type = dinf.DriveFormat.ToString(); + name = dinf.Name; + ConsoleEx.Bold = true; + Console.Write("Drive name: "); + ConsoleEx.Bold = false; + Console.WriteLine(name); + ConsoleEx.Bold = true; + Console.Write("Drive type: "); + ConsoleEx.Bold = false; + Console.WriteLine(type); + ConsoleEx.Bold = true; + Console.Write("Total space: "); + ConsoleEx.Bold = false; + Console.WriteLine(String.Format("{0:F1}", totalBytes) + " GB"); + ConsoleEx.Bold = true; + Console.Write("Free space: "); + Console.WriteLine(String.Format("{0:F1}", bytesFree) + " GB"); + Console.WriteLine(); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } ConsoleEx.Bold = false; ConsoleEx.BackgroundColor = ConsoleColor.Black; diff --git a/ShiftOS_TheReturn/Lunix.cs b/ShiftOS_TheReturn/Lunix.cs new file mode 100644 index 0000000..529cb71 --- /dev/null +++ b/ShiftOS_TheReturn/Lunix.cs @@ -0,0 +1,201 @@ +/* + * 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 Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ShiftOS.Engine +{ + /// + /// Special functions for when we're running under Wine. (Pure Mono is not detected) + /// + public static class Lunix + { + /// + /// Determines if the game is running under Wine or not. + /// + public static bool InWine + { + get + { + var WineKey = Registry.CurrentUser.OpenSubKey(@"Software\Wine"); + return WineKey != null; + } + } + /// + /// Escape Wine and run shell commands. + /// + public static class Bash + { + /// + /// Run a command in Bash on the host OS. + /// + /// The command to run. + /// Everything put on STDOUT by the command. + public static string RunCommand(string command) + { + string outfile = Path.GetRandomFileName(); + string outpath = "Z:\\tmp\\" + outfile; + string copypath = Path.GetTempPath() + Path.GetRandomFileName(); + var p = new Process(); + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.FileName = "cmd"; + p.StartInfo.Arguments = "/C start Z:\\bin\\bash -c \"" + command.Replace("\"", "\\\"") + " > /tmp/" + outfile + "\""; + p.Start(); + p.WaitForExit(); + p.StartInfo.Arguments = "/C move " + outpath + " " + copypath; // Move the file somewhere .NET can see it. + p.Start(); + p.WaitForExit(); + string output = File.ReadAllText(copypath); + File.Delete(copypath); // Get rid of that. + return output; + } + } + /// + /// Basic, unreliable implementation of DriveInfo using df and lsblk. + /// + public class DFDriveInfo + { + private int dfrow, lsblkrow; + public string Name; + private static string[] rundf(string output) + { + return Bash.RunCommand("df --output='" + output + "'").Split('\n'); + } + + private string ourdf(string output) + { + return rundf(output)[dfrow]; + } + + private static string[] runlsblk(string output) + { + return Bash.RunCommand("lsblk -b --output='" + output + "'").Split('\n'); + } + + private string ourlsblk(string output) + { + return runlsblk(output)[lsblkrow]; + } + + public DFDriveInfo(string driveName) + { + dfrow = Array.IndexOf(rundf("target"), driveName); + lsblkrow = Array.IndexOf(runlsblk("MOUNTPOINT"), driveName); + Name = driveName; + } + public long AvailableFreeSpace + { + get + { + return Convert.ToInt64(ourdf("avail")); + } + } + + public string DriveFormat + { + get + { + return Bash.RunCommand("df --output='fstype'").Split('\n')[dfrow]; + } + } + + public DriveType DriveType + { + get + { + return DriveType.Unknown; + } + } + + public bool IsReady + { + get + { + return true; + } + } + + public DirectoryInfo RootDirectory + { + get + { + return new DirectoryInfo("Z:" + Name.Replace('/', '\\')); + } + } + + public long TotalFreeSpace + { + get + { + return AvailableFreeSpace; + } + } + + public long TotalSize + { + get + { + return Convert.ToInt64(ourlsblk("SIZE")); + } + } + + public string VolumeLabel + { + get + { + return ourlsblk("LABEL"); + } + + set + { + throw new NotImplementedException(); + } + } + + public static DFDriveInfo[] GetDrives() + { + string[] dnames = rundf("target"); + var output = new DFDriveInfo[dnames.Length - 1]; + for (int i = 1; i < dnames.Length; i++) + output[i - 1] = new DFDriveInfo(dnames[i]); + return output; + } + + public override string ToString() + { + return Name; + } + } + } +} diff --git a/ShiftOS_TheReturn/ShiftOS.Engine.csproj b/ShiftOS_TheReturn/ShiftOS.Engine.csproj index 31f423c..9c9ad0f 100644 --- a/ShiftOS_TheReturn/ShiftOS.Engine.csproj +++ b/ShiftOS_TheReturn/ShiftOS.Engine.csproj @@ -138,6 +138,7 @@ + -- cgit v1.2.3 From bbbc00f204f77ca547340b010ec21662a62e3203 Mon Sep 17 00:00:00 2001 From: RogueAI42 Date: Sat, 17 Jun 2017 00:31:09 +1000 Subject: Fixed multiple compilations for the Python API Previously, only one assembly could be compiled per startup. You could compile all of your mods by restarting the game over and over, loading the previously compiled mods from the cache. Now, that's not necessary. Oh yeah, also, more bugs in the Linux "port". Yippee. Whoops. --- LinuxLauncher/README.md | 3 +++ ShiftOS_TheReturn/PythonAPI.cs | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'LinuxLauncher') diff --git a/LinuxLauncher/README.md b/LinuxLauncher/README.md index 7ee2a74..6b24dce 100644 --- a/LinuxLauncher/README.md +++ b/LinuxLauncher/README.md @@ -13,6 +13,9 @@ make an AUR package of this script if all goes to plan. enable properly * text input boxes are white on white * the terminal puts an extra newline after displaying the prompt +* Aiden Nirh's cutscene has weird overlapping text +* ShiftLetters seems to have no available word lists. Clicking Play +crashes the game. * the ShiftOS desktop can go in front of applications * there is a blue border from the Wine desktop (I want to change that to black, but I also don't want it showing through while the game is diff --git a/ShiftOS_TheReturn/PythonAPI.cs b/ShiftOS_TheReturn/PythonAPI.cs index 49baa04..cc3798c 100644 --- a/ShiftOS_TheReturn/PythonAPI.cs +++ b/ShiftOS_TheReturn/PythonAPI.cs @@ -167,11 +167,7 @@ namespace ShiftOS.Engine scopes = new Dictionary(); var resman = new System.Resources.ResourceManager("ShiftOS.Engine.Properties.Resources", typeof(Properties.Resources).Assembly); var provider = new CSharpCodeProvider(); - var parameters = new CompilerParameters(); - parameters.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Select(f => f.Location).ToArray()); - parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll"); - parameters.GenerateInMemory = false; // We need to keep the temporary file around long enough to copy it to the cache. - parameters.GenerateExecutable = false; + var refs = AppDomain.CurrentDomain.GetAssemblies().Select(f => f.Location).Concat(new string[] { "Microsoft.CSharp.dll" }).ToArray(); var types = new List(); var sha = new SHA512Managed(); var oldcache = new Dictionary(); @@ -203,6 +199,7 @@ namespace ShiftOS.Engine { Console.WriteLine("[dev] Failed to execute Python script " + fname); Console.WriteLine(ex.ToString()); + continue; } if (oldcache.ContainsKey(fname)) { @@ -229,6 +226,10 @@ namespace ShiftOS.Engine int pos = 0; var entry = new AsmCacheEntry(); entry.checksum = checksum; + var parameters = new CompilerParameters(); + parameters.ReferencedAssemblies.AddRange(refs); + parameters.GenerateInMemory = false; // We need to keep the temporary file around long enough to copy it to the cache. + parameters.GenerateExecutable = false; try { while (pos < scriptlines.Length) @@ -264,7 +265,6 @@ namespace ShiftOS.Engine } types.AddRange(results.CompiledAssembly.GetTypes()); // We did it! entry.asms.Add(File.ReadAllBytes(results.PathToAssembly)); - File.Delete(results.PathToAssembly); pos++; // keep scanning the file for more classes } } -- cgit v1.2.3