From f1ffb129d660476729cf9ed000b089cdcbc78342 Mon Sep 17 00:00:00 2001 From: emmaus Date: Sun, 17 Jun 2018 09:37:34 +0000 Subject: [PATCH] Implemented Gamelist --- Ryujinx.HLE/Loaders/Executables/Nro.cs | 21 +- Ryujinx.HLE/Logging/Logger.cs | 10 + Ryujinx.HLE/VirtualFileSystem.cs | 2 +- Ryujinx.ImGui/Config.cs | 67 +- Ryujinx.ImGui/Extensions/ControlArchive.cs | 53 + Ryujinx.ImGui/Extensions/Nro.cs | 72 ++ Ryujinx.ImGui/GUI/EmulationWindow.cs | 8 +- .../GUI/Widgets/ConfigurationWidget.cs | 34 +- Ryujinx.ImGui/GUI/Widgets/FolderPicker.cs | 127 +++ Ryujinx.ImGui/GUI/Widgets/GameList.cs | 198 ++++ Ryujinx.ImGui/GUI/Widgets/HomeUI.cs | 9 + Ryujinx.ImGui/GUI/Widgets/NanoJpeg.cs | 1008 +++++++++++++++++ Ryujinx.ImGui/GUI/WindowHelper.cs | 9 +- Ryujinx.ImGui/Ryujinx.UI.csproj | 1 + Ryujinx/Properties/launchSettings.json | 8 + 15 files changed, 1611 insertions(+), 16 deletions(-) create mode 100644 Ryujinx.ImGui/Extensions/ControlArchive.cs create mode 100644 Ryujinx.ImGui/Extensions/Nro.cs create mode 100644 Ryujinx.ImGui/GUI/Widgets/FolderPicker.cs create mode 100644 Ryujinx.ImGui/GUI/Widgets/GameList.cs create mode 100644 Ryujinx.ImGui/GUI/Widgets/NanoJpeg.cs create mode 100644 Ryujinx/Properties/launchSettings.json diff --git a/Ryujinx.HLE/Loaders/Executables/Nro.cs b/Ryujinx.HLE/Loaders/Executables/Nro.cs index 0b5068d7b9..4861920c7f 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nro.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nro.cs @@ -1,8 +1,11 @@ +using System; +using System.Drawing; using System.IO; +using System.Text; namespace Ryujinx.HLE.Loaders.Executables { - class Nro : IExecutable + public class Nro : IExecutable { public string FilePath { get; private set; } @@ -15,10 +18,11 @@ namespace Ryujinx.HLE.Loaders.Executables public int ROOffset { get; private set; } public int DataOffset { get; private set; } public int BssSize { get; private set; } - + public int FileSize { get; private set; } + public Nro(Stream Input, string FilePath) { - this.FilePath = FilePath; + this.FilePath = this.FilePath; BinaryReader Reader = new BinaryReader(Input); @@ -39,11 +43,12 @@ namespace Ryujinx.HLE.Loaders.Executables int DataSize = Reader.ReadInt32(); int BssSize = Reader.ReadInt32(); - this.Mod0Offset = Mod0Offset; - this.TextOffset = TextOffset; - this.ROOffset = ROOffset; - this.DataOffset = DataOffset; - this.BssSize = BssSize; + this.Mod0Offset = Mod0Offset; + this.TextOffset = TextOffset; + this.ROOffset = ROOffset; + this.DataOffset = DataOffset; + this.BssSize = BssSize; + this.FileSize = FileSize; byte[] Read(long Position, int Size) { diff --git a/Ryujinx.HLE/Logging/Logger.cs b/Ryujinx.HLE/Logging/Logger.cs index 5376b253a3..8603810ee6 100644 --- a/Ryujinx.HLE/Logging/Logger.cs +++ b/Ryujinx.HLE/Logging/Logger.cs @@ -43,6 +43,16 @@ namespace Ryujinx.HLE.Logging EnabledClasses[(int)Class] = Enabled; } + public bool IsEnabled(LogLevel LogLevel) + { + return EnabledLevels[(int)LogLevel]; + } + + public bool IsFiltered(LogClass LogClass) + { + return EnabledClasses[(int)LogClass]; + } + internal void PrintDebug(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Debug, Class, GetFormattedMessage(Class, Message, Caller)); diff --git a/Ryujinx.HLE/VirtualFileSystem.cs b/Ryujinx.HLE/VirtualFileSystem.cs index df1fc9db13..8fac345c1c 100644 --- a/Ryujinx.HLE/VirtualFileSystem.cs +++ b/Ryujinx.HLE/VirtualFileSystem.cs @@ -3,7 +3,7 @@ using System.IO; namespace Ryujinx.HLE { - class VirtualFileSystem : IDisposable + public class VirtualFileSystem : IDisposable { private const string BasePath = "RyuFs"; private const string NandPath = "nand"; diff --git a/Ryujinx.ImGui/Config.cs b/Ryujinx.ImGui/Config.cs index 0fadb2757a..289da4731a 100644 --- a/Ryujinx.ImGui/Config.cs +++ b/Ryujinx.ImGui/Config.cs @@ -1,4 +1,5 @@ -using Ryujinx.HLE.Input; +using Ryujinx.HLE; +using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -14,6 +15,8 @@ namespace Ryujinx public static string IniPath { get; set; } + public static string DefaultGameDirectory { get; set; } + public static void Read(Logger Log) { string IniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); @@ -92,6 +95,68 @@ namespace Ryujinx ButtonZR = Convert.ToInt16(Parser.GetValue("Controls_Right_FakeJoycon_Button_ZR")) } }; + + DefaultGameDirectory = Parser.GetValue("Default_Game_Directory"); + + if (string.IsNullOrWhiteSpace(DefaultGameDirectory)) + { + VirtualFileSystem FS = new HLE.VirtualFileSystem(); + DefaultGameDirectory = Path.Combine(FS.GetSdCardPath(), "switch"); + } + } + + public static void Save(Logger Log) + { + IniParser Parser = new IniParser(IniPath); + + Parser.SetValue("Enable_Memory_Checks", (!AOptimizations.DisableMemoryChecks).ToString()); + + Parser.SetValue("Logging_Enable_Debug", Log.IsEnabled(LogLevel.Debug).ToString()); + Parser.SetValue("Logging_Enable_Stub", Log.IsEnabled(LogLevel.Stub).ToString()); + Parser.SetValue("Logging_Enable_Info", Log.IsEnabled(LogLevel.Info).ToString()); + Parser.SetValue("Logging_Enable_Warn", Log.IsEnabled(LogLevel.Warning).ToString()); + Parser.SetValue("Logging_Enable_Error", Log.IsEnabled(LogLevel.Error).ToString()); + + + List FilteredClasses = new List(); + + foreach(LogClass LogClass in Enum.GetValues(typeof(LogClass))) + { + if (Log.IsFiltered(LogClass)) + FilteredClasses.Add(LogClass.ToString()); + } + + Parser.SetValue("Logging_Filtered_Classes", string.Join(',', FilteredClasses)); + + Parser.SetValue("Controls_Left_FakeJoycon_Stick_Up", FakeJoyCon.Left.StickUp.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_Stick_Down", FakeJoyCon.Left.StickDown.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_Stick_Left", FakeJoyCon.Left.StickLeft.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_Stick_Right", FakeJoyCon.Left.StickRight.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_Stick_Button", FakeJoyCon.Left.StickButton.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_DPad_Up", FakeJoyCon.Left.DPadUp.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_DPad_Down", FakeJoyCon.Left.DPadDown.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_DPad_Left", FakeJoyCon.Left.DPadLeft.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_DPad_Right", FakeJoyCon.Left.DPadRight.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_Button_Minus", FakeJoyCon.Left.ButtonMinus.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_Button_L", FakeJoyCon.Left.ButtonL.ToString()); + Parser.SetValue("Controls_Left_FakeJoycon_Button_ZL", FakeJoyCon.Left.ButtonZL.ToString()); + + Parser.SetValue("Controls_Right_FakeJoycon_Stick_Up", FakeJoyCon.Right.StickUp.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Stick_Down", FakeJoyCon.Right.StickDown.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Stick_Left", FakeJoyCon.Right.StickLeft.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Stick_Right", FakeJoyCon.Right.StickRight.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Stick_Button", FakeJoyCon.Right.StickButton.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Button_A", FakeJoyCon.Right.StickUp.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Button_B", FakeJoyCon.Right.StickUp.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Button_X", FakeJoyCon.Right.StickUp.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Button_Y", FakeJoyCon.Right.StickUp.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Button_Plus", FakeJoyCon.Right.StickUp.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Button_R", FakeJoyCon.Right.StickUp.ToString()); + Parser.SetValue("Controls_Right_FakeJoycon_Button_ZR", FakeJoyCon.Right.StickUp.ToString()); + + Parser.SetValue("Default_Game_Directory", DefaultGameDirectory); + + Parser.Save(); } } diff --git a/Ryujinx.ImGui/Extensions/ControlArchive.cs b/Ryujinx.ImGui/Extensions/ControlArchive.cs new file mode 100644 index 0000000000..b598e527a7 --- /dev/null +++ b/Ryujinx.ImGui/Extensions/ControlArchive.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Ryujinx.UI +{ + public class ControlArchive + { + public LanguageEntry[] LanguageEntries { get; set; } + public long ApplicationTitleID { get; set; } + public long BaseTitleID { get; set; } + public long ProductCode { get; set; } + public string ApplicationVersion { get; set; } + + public ControlArchive(Stream Input) + { + BinaryReader Reader = new BinaryReader(Input); + + byte[] LanguageEntryData = Reader.ReadBytes(0x3000); + + Input.Seek(0x3060, SeekOrigin.Begin); + ApplicationVersion = Encoding.ASCII.GetString(Reader.ReadBytes(0x10)); + BaseTitleID = Reader.ReadInt64(); + ApplicationTitleID = Reader.ReadInt64(); + + Input.Seek(0x30a8, SeekOrigin.Begin); + ProductCode = Reader.ReadInt64(); + + LanguageEntries = new LanguageEntry[16]; + + using (MemoryStream LanguageStream = new MemoryStream(LanguageEntryData)) + { + BinaryReader LanguageReader = new BinaryReader(LanguageStream); + for (int index = 0; index < 16; index++) + { + LanguageEntries[index] = new LanguageEntry() + { + AplicationName = Encoding.ASCII.GetString(LanguageReader.ReadBytes(0x200)).Trim('\0'), + DeveloperName = Encoding.ASCII.GetString(LanguageReader.ReadBytes(0x100)).Trim('\0') + }; + } + } + } + } + + + public struct LanguageEntry + { + public string AplicationName; + public string DeveloperName; + } +} diff --git a/Ryujinx.ImGui/Extensions/Nro.cs b/Ryujinx.ImGui/Extensions/Nro.cs new file mode 100644 index 0000000000..35f32fa390 --- /dev/null +++ b/Ryujinx.ImGui/Extensions/Nro.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using Ryujinx.HLE.Loaders; + +namespace Ryujinx.UI +{ + class Nro : HLE.Loaders.Executables.Nro + { + public byte[] AssetRomfData { get; set; } + public byte[] IconData { get; set; } + + private byte[] NACPData { get; set; } + + public int AssetOffset { get; set; } + + public ControlArchive ControlArchive { get; set; } + + + public Nro(Stream Input, string Name) : base(Input, Name) + { + BinaryReader Reader = new BinaryReader(Input); + + byte[] Read(long Position, int Size) + { + Input.Seek(Position, SeekOrigin.Begin); + + return Reader.ReadBytes(Size); + } + + if (Input.Length > FileSize) + { + AssetOffset = FileSize; + + string AssetMagic = Encoding.ASCII.GetString(Read(AssetOffset, 4)); + + if (AssetMagic == "ASET") + { + Input.Seek(AssetOffset, SeekOrigin.Begin); + int AssetMagic0 = Reader.ReadInt32(); + int AssetFormat = Reader.ReadInt32(); + byte[] IconSectionInfo = Reader.ReadBytes(0x10); + byte[] NACPSectionInfo = Reader.ReadBytes(0x10); + byte[] AssetRomfSectionInfo = Reader.ReadBytes(0x10); + + long IconOffset = BitConverter.ToInt64(IconSectionInfo, 0); + long IconSize = BitConverter.ToInt64(IconSectionInfo, 8); + long NACPOffset = BitConverter.ToInt64(NACPSectionInfo, 0); + long NACPSize = BitConverter.ToInt64(NACPSectionInfo, 8); + long RomfOffset = BitConverter.ToInt64(AssetRomfSectionInfo, 0); + long RomfSize = BitConverter.ToInt64(AssetRomfSectionInfo, 8); + + Input.Seek(AssetOffset + IconOffset, SeekOrigin.Begin); + IconData = Reader.ReadBytes((int)IconSize); + + Input.Seek(AssetOffset + NACPOffset, SeekOrigin.Begin); + NACPData = Reader.ReadBytes((int)NACPSize); + + Input.Seek(AssetOffset + RomfOffset, SeekOrigin.Begin); + AssetRomfData = Reader.ReadBytes((int)RomfSize); + } + } + + if (NACPData != null) + using (MemoryStream NACPStream = new MemoryStream(NACPData)) + { + ControlArchive = new ControlArchive(NACPStream); + } + } + } +} diff --git a/Ryujinx.ImGui/GUI/EmulationWindow.cs b/Ryujinx.ImGui/GUI/EmulationWindow.cs index 35565ab4e1..9e1f685ece 100644 --- a/Ryujinx.ImGui/GUI/EmulationWindow.cs +++ b/Ryujinx.ImGui/GUI/EmulationWindow.cs @@ -57,7 +57,7 @@ namespace Ryujinx.UI private EmulationController EmulationController; - private Page CurrentPage = Page.PackageLoader; + private Page CurrentPage = Page.GameList; private bool EscapePressed; @@ -74,7 +74,7 @@ namespace Ryujinx.UI IGalRenderer Renderer; IAalOutput AudioOut; - Switch Ns; + public static Switch Ns; public EmulationWindow() : base("Test") { @@ -107,7 +107,7 @@ namespace Ryujinx.UI _deltaTime = (float)e.Time; if (UIActive) { - StartFrame(); + StartFrame(); isRunning = false; if (ShowMainUI) { @@ -119,6 +119,7 @@ namespace Ryujinx.UI showMainUI = false; RenderPauseUI(); } + ImGuiNative.igShowMetricsWindow(ref UIActive); EndFrame(); } else @@ -347,6 +348,7 @@ namespace Ryujinx.UI { Configuration, Emulation, + GameList, PackageLoader } diff --git a/Ryujinx.ImGui/GUI/Widgets/ConfigurationWidget.cs b/Ryujinx.ImGui/GUI/Widgets/ConfigurationWidget.cs index 856841c267..f1251c7b00 100644 --- a/Ryujinx.ImGui/GUI/Widgets/ConfigurationWidget.cs +++ b/Ryujinx.ImGui/GUI/Widgets/ConfigurationWidget.cs @@ -13,11 +13,16 @@ namespace Ryujinx.UI.Widgets public static JoyCon CurrentJoyConLayout; static Page CurrentPage = Page.General; static bool ConfigIntialized = false; + static bool OpenFolderPicker; + static string CurrentPath; static IniParser IniParser; + static FolderPicker FolderPicker; static ConfigurationWidget() { IniParser = new IniParser(Config.IniPath); + FolderPicker = FolderPicker.GetFolderPicker("FolderDialog",Config.DefaultGameDirectory); + CurrentPath = Config.DefaultGameDirectory.ToString(); } public static void Draw() @@ -54,6 +59,33 @@ namespace Ryujinx.UI.Widgets if (ImGui.BeginChild("generalFrame", true, WindowFlags.AlwaysAutoResize)) { ImGui.Text("General Emulation Settings"); + ImGui.Spacing(); + ImGui.LabelText("","Default Game Directory"); + ImGui.SameLine(); + + if( ImGui.Selectable(Config.DefaultGameDirectory)) + { + OpenFolderPicker = true; + } + if (OpenFolderPicker) + ImGui.OpenPopup("OpenFolder"); + + ImGui.SetNextWindowSize(new Vector2(500, 500), Condition.FirstUseEver); + if(ImGui.BeginPopupModal("OpenFolder",WindowFlags.NoResize)) + { + string output = CurrentPath; + if (FolderPicker.Draw(ref output, false)) + { + if (!string.IsNullOrWhiteSpace(output)) + { + Config.DefaultGameDirectory = output; + } + ImGui.CloseCurrentPopup(); + OpenFolderPicker = false; + } + ImGui.EndPopup(); + } + ImGui.Spacing(); ImGui.Checkbox("Disable Cpu Memory Checks", ref AOptimizations.DisableMemoryChecks); ImGui.EndChild(); @@ -83,7 +115,7 @@ namespace Ryujinx.UI.Widgets } if (ImGui.Button("Save", new Vector2(Values.ButtonWidth, Values.ButtonHeight))) { - IniParser.Save(); + Config.Save(EmulationWindow.Ns.Log); } ImGui.SameLine(); if (ImGui.Button("Discard", new Vector2(Values.ButtonWidth, Values.ButtonHeight))) diff --git a/Ryujinx.ImGui/GUI/Widgets/FolderPicker.cs b/Ryujinx.ImGui/GUI/Widgets/FolderPicker.cs new file mode 100644 index 0000000000..9d9039715a --- /dev/null +++ b/Ryujinx.ImGui/GUI/Widgets/FolderPicker.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; + +namespace ImGuiNET +{ + /// + /// Adapted from Mellinoe's file picker for imgui + /// https://github.com/mellinoe/synthapp/blob/master/src/synthapp/Widgets/FilePicker.cs + /// + public class FolderPicker + { + private const string FilePickerID = "###FilePicker"; + private static readonly Dictionary s_folderPickers + = new Dictionary(); + private static readonly Vector2 DefaultFilePickerSize = new Vector2(600, 400); + + public string CurrentFolder { get; set; } + public string SelectedFile { get; set; } + + public static FolderPicker GetFolderPicker(object o, string startingPath) + { + if (File.Exists(startingPath)) + { + startingPath = new FileInfo(startingPath).DirectoryName; + } + else if (string.IsNullOrEmpty(startingPath) || !Directory.Exists(startingPath)) + { + startingPath = Environment.CurrentDirectory; + if (string.IsNullOrEmpty(startingPath)) + { + startingPath = AppContext.BaseDirectory; + } + } + + if (!s_folderPickers.TryGetValue(o, out FolderPicker fp)) + { + fp = new FolderPicker(); + fp.CurrentFolder = startingPath; + s_folderPickers.Add(o, fp); + } + + return fp; + } + + public bool Draw(ref string selected, bool returnOnSelection) + { + bool result = false; + result = DrawFolder(ref selected, returnOnSelection); + return result; + } + + private bool DrawFolder(ref string selected, bool returnOnSelection = false) + { + ImGui.Text("Current Folder: " + CurrentFolder); + bool result = false; + + if (ImGui.BeginChildFrame(1, ImGui.GetContentRegionAvailable() - new Vector2(20, Values.ButtonHeight), + WindowFlags.Default)) + { + DirectoryInfo di = new DirectoryInfo(CurrentFolder); + if (di.Exists) + { + if (di.Parent != null) + { + ImGui.PushStyleColor(ColorTarget.Text, Values.Color.Yellow); + + if (ImGui.Selectable("../", false, SelectableFlags.DontClosePopups + , new Vector2(ImGui.GetContentRegionAvailableWidth(), Values.SelectibleHeight))) + { + CurrentFolder = di.Parent.FullName; + } + + ImGui.PopStyleColor(); + } + foreach (var dir in Directory.EnumerateFileSystemEntries(di.FullName)) + { + if (Directory.Exists(dir)) + { + string name = Path.GetFileName(dir); + bool isSelected = SelectedFile == dir; + + ImGui.PushStyleColor(ColorTarget.Text, Values.Color.Yellow); + + if (ImGui.Selectable(name + "/", isSelected, SelectableFlags.DontClosePopups + , new Vector2(ImGui.GetContentRegionAvailableWidth(), Values.SelectibleHeight))) + { + SelectedFile = dir; + selected = SelectedFile; + } + + if (SelectedFile != null) + if (ImGui.IsMouseDoubleClicked(0) && SelectedFile.Equals(dir)) + { + SelectedFile = null; + selected = null; + CurrentFolder = dir; + } + + ImGui.PopStyleColor(); + } + } + } + } + ImGui.EndChildFrame(); + + + if (ImGui.Button("Cancel", new Vector2(Values.ButtonWidth, Values.ButtonHeight))) + { + result = false; + } + + if (SelectedFile != null) + { + ImGui.SameLine(); + if (ImGui.Button("Open", new Vector2(Values.ButtonWidth, Values.ButtonHeight))) + { + result = true; + selected = SelectedFile; + } + } + + return result; + } + } +} diff --git a/Ryujinx.ImGui/GUI/Widgets/GameList.cs b/Ryujinx.ImGui/GUI/Widgets/GameList.cs new file mode 100644 index 0000000000..d4a3ce4530 --- /dev/null +++ b/Ryujinx.ImGui/GUI/Widgets/GameList.cs @@ -0,0 +1,198 @@ +using System; +using System.IO; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections.Generic; +using System.Text; +using ImGuiNET; +using System.Numerics; +using NanoJpeg; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.UI.Widgets +{ + class GameList + { + static List GameItems = new List(); + static GameItem SelectedGame; + static bool OpenFolderPicker; + static FolderPicker FolderPicker; + static string CurrentPath; + + static GameList() + { + Refresh(Config.DefaultGameDirectory); + FolderPicker = FolderPicker.GetFolderPicker("FolderDialog", Config.DefaultGameDirectory); + } + + public unsafe static void Refresh(string Path) + { + GameItems = new List(); + foreach (string entry in Directory.EnumerateFileSystemEntries(Path)) + { + if (File.Exists(entry)) + { + string Extension = System.IO.Path.GetExtension(entry).ToLower(); + if (Extension == ".nro" || Extension == ".nso") + { + GameItem GameItem = new GameItem(entry); + if (GameItem.IsNro && GameItem.HasIcon) + { + GameItem.TextureID = GL.GenTexture(); + GL.BindTexture(TextureTarget.Texture2D, GameItem.TextureID); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear); + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); + NanoJpeg.NJImage image = new NJImage(); + image.Decode(GameItem.GetIconData()); + GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb, image.Width, image.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgb, + PixelType.UnsignedByte, new IntPtr(image.Image)); + image.Dispose(); + GL.BindTexture(TextureTarget.Texture2D, 0); + } + GameItems.Add(GameItem); + } + } + else if (Directory.Exists(Path)) + { + + } + + } + } + + public unsafe static void DrawList() + { + uint id = 100; + if (ImGui.Button("Refresh GameList")) + Refresh(Config.DefaultGameDirectory); + ImGui.SameLine(); + if(ImGui.Button("Select Game Directory")) + { + OpenFolderPicker = true; + } + if (OpenFolderPicker) + ImGui.OpenPopup("OpenFolder"); + + ImGui.SetNextWindowSize(new Vector2(500, 500), Condition.FirstUseEver); + if (ImGui.BeginPopupModal("OpenFolder", WindowFlags.NoResize)) + { + string output = CurrentPath; + if (FolderPicker.Draw(ref output, false)) + { + if (!string.IsNullOrWhiteSpace(output)) + { + Config.DefaultGameDirectory = output; + Refresh(output); + } + ImGui.CloseCurrentPopup(); + OpenFolderPicker = false; + } + ImGui.EndPopup(); + } + if (ImGui.BeginChildFrame(20, ImGui.GetContentRegionAvailable(), WindowFlags.AlwaysAutoResize)) + { + foreach (GameItem GameItem in GameItems) + { + id++; + if (GameItem == SelectedGame) + ImGui.PushStyleColor(ColorTarget.FrameBg, Values.Color.Yellow); + if (ImGui.BeginChildFrame(id, new Vector2(ImGui.GetContentRegionAvailableWidth(), 60) + , WindowFlags.AlwaysAutoResize)) + { + if (GameItem.IsNro && GameItem.HasIcon) + { + ImGui.Image(new IntPtr(GameItem.TextureID), new Vector2(50, 50), new Vector2(0, 0), + new Vector2(1, 1), new Vector4(255, 255, 255, 255), new Vector4(0, 0, 0, 255)); + } + else + { + ImGui.BeginChildFrame(id + 500, new Vector2(50, 50), WindowFlags.NoResize); + ImGui.EndChildFrame(); + ImGui.SameLine(); + ImGui.Text(Path.GetFileName(GameItem.Path)); + + } + ImGui.SameLine(); + ImGuiNative.igBeginGroup(); + if (GameItem.IsNro) + { + if (GameItem.Nro.ControlArchive != null) + { + ImGui.Text(GameItem.Nro.ControlArchive.LanguageEntries[0].AplicationName); + ImGui.Text(GameItem.Nro.ControlArchive.LanguageEntries[0].DeveloperName); + } + + } + ImGuiNative.igEndGroup(); + if (GameItem == SelectedGame) + ImGui.PopStyleColor(); + if (ImGui.IsMouseDoubleClicked(0) && ImGui.IsItemHovered(HoveredFlags.AllowWhenOverlapped) && GameItem == SelectedGame) + { + + } + else if (ImGui.IsMouseClicked(0) && ImGui.IsItemHovered(HoveredFlags.AllowWhenOverlapped | HoveredFlags.RootAndChildWindows)) + { + SelectedGame = GameItem; + } + ImGui.EndChildFrame(); + } + } + + ImGui.EndChildFrame(); + } + } + } + + class GameItem + { + public AppletType AppletType; + public string Path; + public Nro Nro; + public bool IsNro; + public bool HasIcon => Nro?.IconData != null; + public int TextureID; + + public GameItem(string Path) + { + this.Path = Path; + + if (File.Exists(Path)) + { + AppletType = AppletType.Homebrew; + FileInfo Package = new FileInfo(Path); + if (Package.Extension.ToLower() == ".nro") + { + IsNro = true; + Nro = new Nro(File.Open(Path, FileMode.Open), new FileInfo(Path).Name); + } + } + else + AppletType = AppletType.Cartridge; + } + + public Bitmap GetBitmap() + { + if (IsNro) + { + return new Bitmap(new Bitmap(new MemoryStream(Nro.IconData)),new Size(50,50)); + } + else return null; + } + + public byte[] GetIconData() + { + if (IsNro) + { + return Nro.IconData; + } + else return null; + } + } + + enum AppletType + { + Homebrew, + Cartridge + } +} diff --git a/Ryujinx.ImGui/GUI/Widgets/HomeUI.cs b/Ryujinx.ImGui/GUI/Widgets/HomeUI.cs index 8a25240a99..c53dfb2732 100644 --- a/Ryujinx.ImGui/GUI/Widgets/HomeUI.cs +++ b/Ryujinx.ImGui/GUI/Widgets/HomeUI.cs @@ -24,6 +24,12 @@ namespace Ryujinx.UI CurrentPage = Page.PackageLoader; } + if (ImGui.Button("Game List", new System.Numerics.Vector2(Values.ButtonWidth, + Values.ButtonHeight))) + { + CurrentPage = Page.GameList; + } + if (ImGui.Button("Settings", new System.Numerics.Vector2(Values.ButtonWidth, Values.ButtonHeight))) { @@ -54,6 +60,9 @@ namespace Ryujinx.UI case Page.Configuration: Widgets.ConfigurationWidget.Draw(); break; + case Page.GameList: + Widgets.GameList.DrawList(); + break; } ImGui.EndChildFrame(); } diff --git a/Ryujinx.ImGui/GUI/Widgets/NanoJpeg.cs b/Ryujinx.ImGui/GUI/Widgets/NanoJpeg.cs new file mode 100644 index 0000000000..fc7b708434 --- /dev/null +++ b/Ryujinx.ImGui/GUI/Widgets/NanoJpeg.cs @@ -0,0 +1,1008 @@ +/////////////////////////////////////////////////////////////////////////////// +// INFO // +/////////////////////////////////////////////////////////////////////////////// + +// NanoJPEG -- Unmanaged C# Port +// version 1.0.0 (10-Aug-2015) +// by Johannes Bildstein +// original license below + +/////////////////////////////////////////////////////////////////////////////// +// START ORIGINAL LICENSE SECTION // +/////////////////////////////////////////////////////////////////////////////// + +// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder +// version 1.3.2 (2014-02-02) +// by Martin J. Fiedler +// +// This software is published under the terms of KeyJ's Research License, +// version 0.2. Usage of this software is subject to the following conditions: +// 0. There's no warranty whatsoever. The author(s) of this software can not +// be held liable for any damages that occur when using this software. +// 1. This software may be used freely for both non-commercial and commercial +// purposes. +// 2. This software may be redistributed freely as long as no fees are charged +// for the distribution and this license information is included. +// 3. This software may be modified freely except for this license information, +// which must not be changed in any way. +// 4. If anything other than configuration, indentation or comments have been +// altered in the code, the original author(s) must receive a copy of the +// modified code. + +/////////////////////////////////////////////////////////////////////////////// +// END ORIGINAL LICENSE SECTION // +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace NanoJpeg +{ + /// + /// Error codes for decoding errors + /// + public enum NJErrorCode + { + /// + /// Not a JPEG file + /// + NoJpeg, + /// + /// Unsupported format + /// + Unsupported, + /// + /// Internal error + /// + InternalError, + /// + /// Syntax error + /// + SyntaxError, + } + + /// + /// Exception for decoding errors + /// + public class NJException : Exception + { + /// + /// The error code of this exception + /// + public NJErrorCode ErrorCode + { + get { return _ErrorCode; } + } + private NJErrorCode _ErrorCode = NJErrorCode.InternalError; + + /// + /// Creates a new instance of the class + /// + /// The error code of this exception + public NJException(NJErrorCode ErrorCode) + : base(ErrorCode.ToString()) + { + _ErrorCode = ErrorCode; + } + } + + /// + /// Provides methods to decode a Jpeg image. + /// NOT thread safe + /// + public unsafe sealed class NJImage : IDisposable + { + #region Variables/Constants + + private bool IsDisposed; + private bool IsDone; + + private byte* pos; + private int size; + private int length; + private int width, height; + private int mbwidth, mbheight; + private int mbsizex, mbsizey; + private int ncomp; + private Component* comp; + private int qtused, qtavail; + private byte** qtab; + private VLCCode** vlctab; + private int buf, bufbits; + private int* block; + private int rstinterval; + private byte* rgb; + + const int W1 = 2841; + const int W2 = 2676; + const int W3 = 2408; + const int W5 = 1609; + const int W6 = 1108; + const int W7 = 565; + + const int CF4A = -9; + const int CF4B = 111; + const int CF4C = 29; + const int CF4D = -3; + const int CF3A = 28; + const int CF3B = 109; + const int CF3C = -9; + const int CF3X = 104; + const int CF3Y = 27; + const int CF3Z = -3; + const int CF2A = 139; + const int CF2B = -11; + + static byte[] njZZ = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, +11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, +42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, +38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; + + #endregion + + #region Properties + + /// + /// The width of the last decoded image + /// + public int Width + { + get { return width; } + } + /// + /// The height of the last decoded image + /// + public int Height + { + get { return height; } + } + /// + /// States if the last decoded image is a color (RGB) or grayscale image + /// + public bool IsColor + { + get { return ncomp != 1; } + } + /// + /// Pointer to the pixels + /// + public byte* Image + { + get + { + if (ncomp == 1) return comp[0].pixels; + else return rgb; + } + } + /// + /// Size of the image in bytes + /// + public int ImageSize + { + get { return width * height * ncomp; } + } + + #endregion + + #region Helper Structs + + private struct VLCCode + { + public byte bits; + public byte code; + } + + private struct Component + { + public int cid; + public int ssx; + public int ssy; + public int width; + public int height; + public int stride; + public int qtsel; + public int actabsel; + public int dctabsel; + public int dcpred; + public byte* pixels; + } + + #endregion + + #region Init/Dispose + + /// + /// Creates a new instance of the class + /// + public NJImage() + { + comp = (Component*)Marshal.AllocHGlobal(3 * Marshal.SizeOf(typeof(Component))); + block = (int*)Marshal.AllocHGlobal(64 * Marshal.SizeOf(typeof(int))); + + FillMem(comp, new Component(), 3); + + qtab = (byte**)Marshal.AllocHGlobal(4 * IntPtr.Size); + vlctab = (VLCCode**)Marshal.AllocHGlobal(4 * IntPtr.Size); + for (int i = 0; i < 4; i++) + { + qtab[i] = (byte*)Marshal.AllocHGlobal(64 * Marshal.SizeOf(typeof(byte))); + vlctab[i] = (VLCCode*)Marshal.AllocHGlobal(65536 * Marshal.SizeOf(typeof(VLCCode))); + + FillMem((long*)qtab[i], 0, 64 / 8);//use long instead of byte + FillMem((long*)vlctab[i], 0, 65536 / 4);//use long instead of VLCCode (length=2) + } + } + + /// + /// Finalizer of the class + /// + ~NJImage() + { + Dispose(false); + } + + /// + /// Releases all allocated resources + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool managed) + { + if (!IsDisposed) + { + if (rgb != null) + { + Marshal.FreeHGlobal((IntPtr)rgb); + rgb = null; + } + + for (int i = 0; i < 4; i++) + { + if (qtab[i] != null) Marshal.FreeHGlobal((IntPtr)qtab[i]); + if (vlctab[i] != null) Marshal.FreeHGlobal((IntPtr)vlctab[i]); + } + + if (qtab != null) Marshal.FreeHGlobal((IntPtr)qtab); + if (vlctab != null) Marshal.FreeHGlobal((IntPtr)vlctab); + if (comp != null) Marshal.FreeHGlobal((IntPtr)comp); + if (block != null) Marshal.FreeHGlobal((IntPtr)block); + + IsDisposed = true; + } + } + + #endregion + + #region Public Methods + + /// + /// Decodes a jpeg image + /// + /// Path to the image file + public void Decode(string jpeg) + { + byte[] data = File.ReadAllBytes(jpeg); + fixed (byte* ptr = data) + { + Decode(ptr, data.Length, false); + } + } + + /// + /// Decodes a jpeg image + /// + /// The stream that contains the jpeg data + public void Decode(Stream jpeg) + { + byte[] data = new byte[jpeg.Length]; + jpeg.Read(data, 0, (int)jpeg.Length); + + fixed (byte* ptr = data) + { + Decode(ptr, data.Length, false); + } + } + + /// + /// Decodes a jpeg image + /// + /// The stream that contains the jpeg data + public void Decode(MemoryStream jpeg) + { + fixed (byte* ptr = jpeg.GetBuffer()) + { + Decode(ptr, (int)jpeg.Length, false); + } + } + + /// + /// Decodes a jpeg image + /// + /// The stream that contains the jpeg data + public void Decode(UnmanagedMemoryStream jpeg) + { + Decode(jpeg.PositionPointer, (int)jpeg.Length, false); + } + + /// + /// Decodes a jpeg image + /// + /// + public void Decode(byte[] jpeg) + { + fixed(byte* ptr = jpeg) + { + Decode(ptr, jpeg.Length, false); + } + } + + /// + /// Decodes a jpeg image + /// + /// Pointer to the jpeg data + /// Size of the jpeg data in bytes + public void Decode(byte* jpeg, int size) + { + Decode(jpeg, size, false); + } + + /// + /// Decodes a jpeg image + /// + /// Pointer to the jpeg data + /// Size of the jpeg data in bytes + /// True to flip the Red and Blue channel. This is useful for System.Drawing (GDI+) classes. + public void Decode(byte* jpeg, int size, bool flip) + { + Init(); + + this.pos = jpeg; + this.size = size & 0x7FFFFFFF; + if (this.size < 2) throw new NJException(NJErrorCode.NoJpeg); + if (((this.pos[0] ^ 0xFF) | (this.pos[1] ^ 0xD8)) != 0) throw new NJException(NJErrorCode.NoJpeg); + Skip(2); + while (!IsDone) + { + if ((this.size < 2) || (this.pos[0] != 0xFF)) throw new NJException(NJErrorCode.SyntaxError); + Skip(2); + switch (this.pos[-1]) + { + case 0xC0: DecodeSOF(); break; + case 0xC4: DecodeDHT(); break; + case 0xDB: DecodeDQT(); break; + case 0xDD: DecodeDRI(); break; + case 0xDA: DecodeScan(); break; + case 0xFE: SkipMarker(); break; + default: + if ((this.pos[-1] & 0xF0) == 0xE0) SkipMarker(); + else throw new NJException(NJErrorCode.Unsupported); + break; + } + } + ConvertYCC(flip); + } + + #endregion + + #region Private Methods + + private void Init() + { + if (rgb != null) + { + Marshal.FreeHGlobal((IntPtr)rgb); + rgb = null; + } + + FillMem(comp, new Component(), 3); + IsDone = false; + bufbits = 0; + } + + private void RowIDCT(int* blk) + { + int x0, x1, x2, x3, x4, x5, x6, x7, x8; + if (((x1 = blk[4] << 11) + | (x2 = blk[6]) + | (x3 = blk[2]) + | (x4 = blk[1]) + | (x5 = blk[7]) + | (x6 = blk[5]) + | (x7 = blk[3])) == 0) + { + blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3; + return; + } + x0 = (blk[0] << 11) + 128; + x8 = W7 * (x4 + x5); + x4 = x8 + (W1 - W7) * x4; + x5 = x8 - (W1 + W7) * x5; + x8 = W3 * (x6 + x7); + x6 = x8 - (W3 - W5) * x6; + x7 = x8 - (W3 + W5) * x7; + x8 = x0 + x1; + x0 -= x1; + x1 = W6 * (x3 + x2); + x2 = x1 - (W2 + W6) * x2; + x3 = x1 + (W2 - W6) * x3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (181 * (x4 + x5) + 128) >> 8; + x4 = (181 * (x4 - x5) + 128) >> 8; + blk[0] = (x7 + x1) >> 8; + blk[1] = (x3 + x2) >> 8; + blk[2] = (x0 + x4) >> 8; + blk[3] = (x8 + x6) >> 8; + blk[4] = (x8 - x6) >> 8; + blk[5] = (x0 - x4) >> 8; + blk[6] = (x3 - x2) >> 8; + blk[7] = (x7 - x1) >> 8; + } + + private void ColIDCT(int* blk, byte* outv, int stride) + { + int x0, x1, x2, x3, x4, x5, x6, x7, x8; + if (((x1 = blk[8 * 4] << 8) + | (x2 = blk[8 * 6]) + | (x3 = blk[8 * 2]) + | (x4 = blk[8 * 1]) + | (x5 = blk[8 * 7]) + | (x6 = blk[8 * 5]) + | (x7 = blk[8 * 3])) == 0) + { + x1 = Clip(((blk[0] + 32) >> 6) + 128); + for (x0 = 8; x0 != 0; --x0) + { + *outv = (byte)x1; + outv += stride; + } + return; + } + x0 = (blk[0] << 8) + 8192; + x8 = W7 * (x4 + x5) + 4; + x4 = (x8 + (W1 - W7) * x4) >> 3; + x5 = (x8 - (W1 + W7) * x5) >> 3; + x8 = W3 * (x6 + x7) + 4; + x6 = (x8 - (W3 - W5) * x6) >> 3; + x7 = (x8 - (W3 + W5) * x7) >> 3; + x8 = x0 + x1; + x0 -= x1; + x1 = W6 * (x3 + x2) + 4; + x2 = (x1 - (W2 + W6) * x2) >> 3; + x3 = (x1 + (W2 - W6) * x3) >> 3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (181 * (x4 + x5) + 128) >> 8; + x4 = (181 * (x4 - x5) + 128) >> 8; + *outv = Clip(((x7 + x1) >> 14) + 128); outv += stride; + *outv = Clip(((x3 + x2) >> 14) + 128); outv += stride; + *outv = Clip(((x0 + x4) >> 14) + 128); outv += stride; + *outv = Clip(((x8 + x6) >> 14) + 128); outv += stride; + *outv = Clip(((x8 - x6) >> 14) + 128); outv += stride; + *outv = Clip(((x0 - x4) >> 14) + 128); outv += stride; + *outv = Clip(((x3 - x2) >> 14) + 128); outv += stride; + *outv = Clip(((x7 - x1) >> 14) + 128); + } + + private int ShowBits(int bits) + { + byte newbyte; + if (bits == 0) return 0; + while (this.bufbits < bits) + { + if (this.size <= 0) + { + this.buf = (this.buf << 8) | 0xFF; + this.bufbits += 8; + continue; + } + newbyte = *this.pos++; + this.size--; + this.bufbits += 8; + this.buf = (this.buf << 8) | newbyte; + if (newbyte == 0xFF) + { + if (this.size != 0) + { + byte marker = *this.pos++; + this.size--; + switch (marker) + { + case 0x00: + case 0xFF: + break; + case 0xD9: this.size = 0; break; + default: + if ((marker & 0xF8) != 0xD0) throw new NJException(NJErrorCode.SyntaxError); + else + { + this.buf = (this.buf << 8) | marker; + this.bufbits += 8; + } + break; + } + } + else throw new NJException(NJErrorCode.SyntaxError); + } + } + return (this.buf >> (this.bufbits - bits)) & ((1 << bits) - 1); + } + + private void DecodeSOF() + { + int i, ssxmax = 0, ssymax = 0; + Component* c; + DecodeLength(); + if (this.length < 9) throw new NJException(NJErrorCode.SyntaxError); + if (this.pos[0] != 8) throw new NJException(NJErrorCode.Unsupported); + this.height = Decode16(this.pos + 1); + this.width = Decode16(this.pos + 3); + this.ncomp = this.pos[5]; + Skip(6); + switch (this.ncomp) + { + case 1: + case 3: + break; + default: + throw new NJException(NJErrorCode.Unsupported); + } + if (this.length < (this.ncomp * 3)) throw new NJException(NJErrorCode.SyntaxError); + for (i = 0, c = this.comp; i < this.ncomp; ++i, ++c) + { + c->cid = this.pos[0]; + if ((c->ssx = this.pos[1] >> 4) == 0) throw new NJException(NJErrorCode.SyntaxError); + if ((c->ssx & (c->ssx - 1)) != 0) throw new NJException(NJErrorCode.Unsupported); // non-power of two + if ((c->ssy = this.pos[1] & 15) == 0) throw new NJException(NJErrorCode.SyntaxError); + if ((c->ssy & (c->ssy - 1)) != 0) throw new NJException(NJErrorCode.Unsupported); // non-power of two + if (((c->qtsel = this.pos[2]) & 0xFC) != 0) throw new NJException(NJErrorCode.SyntaxError); + Skip(3); + this.qtused |= 1 << c->qtsel; + if (c->ssx > ssxmax) ssxmax = c->ssx; + if (c->ssy > ssymax) ssymax = c->ssy; + } + if (this.ncomp == 1) + { + c = this.comp; + c->ssx = c->ssy = ssxmax = ssymax = 1; + } + this.mbsizex = ssxmax << 3; + this.mbsizey = ssymax << 3; + this.mbwidth = (this.width + this.mbsizex - 1) / this.mbsizex; + this.mbheight = (this.height + this.mbsizey - 1) / this.mbsizey; + for (i = 0, c = this.comp; i < this.ncomp; ++i, ++c) + { + c->width = (this.width * c->ssx + ssxmax - 1) / ssxmax; + c->height = (this.height * c->ssy + ssymax - 1) / ssymax; + c->stride = this.mbwidth * c->ssx << 3; + if (((c->width < 3) && (c->ssx != ssxmax)) || ((c->height < 3) && (c->ssy != ssymax))) throw new NJException(NJErrorCode.Unsupported); + c->pixels = (byte*)Marshal.AllocHGlobal(c->stride * this.mbheight * c->ssy << 3); + } + if (this.ncomp == 3) this.rgb = (byte*)Marshal.AllocHGlobal(this.width * this.height * this.ncomp); + Skip(this.length); + } + + private void DecodeDHT() + { + int codelen, currcnt, remain, spread, i, j; + VLCCode* vlc; + byte* counts = stackalloc byte[16]; + DecodeLength(); + while (this.length >= 17) + { + i = this.pos[0]; + if ((i & 0xEC) != 0) throw new NJException(NJErrorCode.SyntaxError); + if ((i & 0x02) != 0) throw new NJException(NJErrorCode.Unsupported); + i = (i | (i >> 3)) & 3; // combined DC/AC + tableid value + for (codelen = 1; codelen <= 16; ++codelen) + counts[codelen - 1] = this.pos[codelen]; + Skip(17); + vlc = &this.vlctab[i][0]; + remain = spread = 65536; + for (codelen = 1; codelen <= 16; ++codelen) + { + spread >>= 1; + currcnt = counts[codelen - 1]; + if (currcnt == 0) continue; + if (this.length < currcnt) throw new NJException(NJErrorCode.SyntaxError); + remain -= currcnt << (16 - codelen); + if (remain < 0) throw new NJException(NJErrorCode.SyntaxError); + for (i = 0; i < currcnt; ++i) + { + byte code = this.pos[i]; + for (j = spread; j != 0; --j) + { + vlc->bits = (byte)codelen; + vlc->code = code; + ++vlc; + } + } + Skip(currcnt); + } + while (remain-- != 0) + { + vlc->bits = 0; + ++vlc; + } + } + if (this.length != 0) throw new NJException(NJErrorCode.SyntaxError); + } + + private void DecodeDQT() + { + int i; + byte* t; + DecodeLength(); + while (this.length >= 65) + { + i = this.pos[0]; + if ((i & 0xFC) != 0) throw new NJException(NJErrorCode.SyntaxError); + this.qtavail |= 1 << i; + t = &this.qtab[i][0]; + for (i = 0; i < 64; ++i) t[i] = this.pos[i + 1]; + Skip(65); + } + if (this.length != 0) throw new NJException(NJErrorCode.SyntaxError); + } + + private int GetVLC(VLCCode* vlc, byte* code) + { + int value = ShowBits(16); + int bits = vlc[value].bits; + if (bits == 0) throw new NJException(NJErrorCode.SyntaxError); + SkipBits(bits); + value = vlc[value].code; + if (code != null) *code = (byte)value; + bits = value & 15; + if (bits == 0) return 0; + value = GetBits(bits); + if (value < (1 << (bits - 1))) value += ((-1) << bits) + 1; + return value; + } + + private void DecodeBlock(Component* c, byte* outv) + { + byte code = 0; + int value, coef = 0; + FillMem((long*)block, 0, 64 / 2);//use long instead of int + c->dcpred += GetVLC(&this.vlctab[c->dctabsel][0], null); + this.block[0] = (c->dcpred) * this.qtab[c->qtsel][0]; + do + { + value = GetVLC(&this.vlctab[c->actabsel][0], &code); + if (code == 0) break; // EOB + if ((code & 0x0F) == 0 && code != 0xF0) throw new NJException(NJErrorCode.SyntaxError); + coef += (code >> 4) + 1; + if (coef > 63) throw new NJException(NJErrorCode.SyntaxError); + this.block[(int)njZZ[coef]] = value * this.qtab[c->qtsel][coef]; + } while (coef < 63); + for (coef = 0; coef < 64; coef += 8) { RowIDCT(&this.block[coef]); } + for (coef = 0; coef < 8; ++coef) { ColIDCT(&this.block[coef], &outv[coef], c->stride); } + } + + private void DecodeScan() + { + int i, mbx, mby, sbx, sby; + int rstcount = this.rstinterval, nextrst = 0; + Component* c; + DecodeLength(); + if (this.length < (4 + 2 * this.ncomp)) throw new NJException(NJErrorCode.SyntaxError); + if (this.pos[0] != this.ncomp) throw new NJException(NJErrorCode.Unsupported); + Skip(1); + for (i = 0, c = this.comp; i < this.ncomp; ++i, ++c) + { + if (this.pos[0] != c->cid) throw new NJException(NJErrorCode.SyntaxError); + if ((this.pos[1] & 0xEE) != 0) throw new NJException(NJErrorCode.SyntaxError); + c->dctabsel = this.pos[1] >> 4; + c->actabsel = (this.pos[1] & 1) | 2; + Skip(2); + } + if (this.pos[0] != 0 || this.pos[1] != 63 || this.pos[2] != 0) throw new NJException(NJErrorCode.Unsupported); + Skip(this.length); + mbx = mby = 0; + while(true) + { + for (i = 0, c = this.comp; i < this.ncomp; ++i, ++c) + { + for (sby = 0; sby < c->ssy; ++sby) + { + for (sbx = 0; sbx < c->ssx; ++sbx) + { + DecodeBlock(c, &c->pixels[((mby * c->ssy + sby) * c->stride + mbx * c->ssx + sbx) << 3]); + } + } + } + if (++mbx >= this.mbwidth) + { + mbx = 0; + if (++mby >= this.mbheight) break; + } + if (this.rstinterval != 0 && --rstcount == 0) + { + this.bufbits &= 0xF8; + i = GetBits(16); + if ((i & 0xFFF8) != 0xFFD0 || (i & 7) != nextrst) throw new NJException(NJErrorCode.SyntaxError); + nextrst = (nextrst + 1) & 7; + rstcount = this.rstinterval; + for (i = 0; i < 3; ++i) { this.comp[i].dcpred = 0; } + } + } + IsDone = true; + } + + private void UpsampleH(Component* c) + { + int xmax = c->width - 3; + byte* outv, lin, lout; + int x, y; + try + { + outv = (byte*)Marshal.AllocHGlobal((c->width * c->height) << 1); + lin = c->pixels; + lout = outv; + for (y = c->height; y != 0; --y) + { + lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]); + lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]); + lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]); + + for (x = 0; x < xmax; ++x) + { + lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]); + lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]); + } + + lin += c->stride; + lout += c->width << 1; + + lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]); + lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]); + lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]); + } + c->width <<= 1; + c->stride = c->width; + } + finally { if (c->pixels != null) Marshal.FreeHGlobal((IntPtr)c->pixels); } + c->pixels = outv; + } + + private void UpsampleV(Component* c) + { + int w = c->width, s1 = c->stride, s2 = s1 + s1; + byte* outv, cin, cout; + int x, y; + try + { + outv = (byte*)Marshal.AllocHGlobal((c->width * c->height) << 1); + for (x = 0; x < w; ++x) + { + cin = &c->pixels[x]; + cout = &outv[x]; + *cout = CF(CF2A * cin[0] + CF2B * cin[s1]); cout += w; + *cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]); cout += w; + *cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]); cout += w; + cin += s1; + for (y = c->height - 3; y != 0; --y) + { + *cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]); cout += w; + *cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]); cout += w; + cin += s1; + } + cin += s1; + *cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]); cout += w; + *cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]); cout += w; + *cout = CF(CF2A * cin[0] + CF2B * cin[-s1]); + } + c->height <<= 1; + c->stride = c->width; + } + finally { if (c->pixels != null) Marshal.FreeHGlobal((IntPtr)c->pixels); } + c->pixels = outv; + } + + private void ConvertYCC(bool flip) + { + int i; + int w = this.width; + int h = this.height; + Component* c; + + for (i = 0, c = this.comp; i < this.ncomp; ++i, ++c) + { + while ((c->width < w) || (c->height < h)) + { + if (c->width < w) UpsampleH(c); + if (c->height < h) UpsampleV(c); + } + if ((c->width < w) || (c->height < h)) throw new NJException(NJErrorCode.InternalError); + } + + if (this.ncomp == 3) + { + // convert to RGB + int x, yy, y, cb, cr, r, g, b; + byte* prgb = this.rgb; + byte* py = this.comp[0].pixels; + byte* pcb = this.comp[1].pixels; + byte* pcr = this.comp[2].pixels; + int rs = this.comp[0].stride - w; + int gs = this.comp[1].stride - w; + int bs = this.comp[2].stride - w; + + for (yy = this.height; yy != 0; --yy) + { + for (x = 0; x < w; ++x) + { + y = *py++ << 8; + cb = *pcb++ - 128; + cr = *pcr++ - 128; + + g = (y - 88 * cb - 183 * cr + 128) >> 8; + + if (flip) + { + b = (y + 359 * cr + 128) >> 8; + r = (y + 454 * cb + 128) >> 8; + } + else + { + r = (y + 359 * cr + 128) >> 8; + b = (y + 454 * cb + 128) >> 8; + } + + if (r < 0) *prgb++ = 0; + else if (r > 0xFF) *prgb++ = 0xFF; + else *prgb++ = (byte)r; + + if (g < 0) *prgb++ = 0; + else if (g > 0xFF) *prgb++ = 0xFF; + else *prgb++ = (byte)g; + + if (b < 0) *prgb++ = 0; + else if (b > 0xFF) *prgb++ = 0xFF; + else *prgb++ = (byte)b; + } + py += rs; + pcb += gs; + pcr += bs; + } + + Marshal.FreeHGlobal((IntPtr)this.comp[0].pixels); + Marshal.FreeHGlobal((IntPtr)this.comp[1].pixels); + Marshal.FreeHGlobal((IntPtr)this.comp[2].pixels); + this.comp[0].pixels = this.comp[1].pixels = this.comp[2].pixels = null; + } + else if (this.comp[0].width != this.comp[0].stride) + { + // grayscale -> only remove stride + int y, x; + int cw = this.comp[0].width; + int cs = this.comp[0].stride; + int d = cs - cw; + byte* pin = &this.comp[0].pixels[cs]; + byte* pout = &this.comp[0].pixels[cw]; + + for (y = this.comp[0].height - 1; y != 0; --y) + { + for (x = 0; x < cw; x++) *pout++ = *pin++; + pin += d; + } + this.comp[0].stride = cw; + + Marshal.FreeHGlobal((IntPtr)this.comp[0].pixels); + this.comp[0].pixels = null; + } + } + + #endregion + + #region Helper Methods + + private static byte Clip(int x) + { + if (x < 0) return 0; + else if (x > 0xFF) return 0xFF; + else return (byte)x; + } + + private static byte CF(int x) + { + x = (x + 64) >> 7; + if (x < 0) return 0; + else if (x > 0xFF) return 0xFF; + else return (byte)x; + } + + private void SkipBits(int bits) + { + if (this.bufbits < bits) ShowBits(bits); + this.bufbits -= bits; + } + + private int GetBits(int bits) + { + int res = ShowBits(bits); + SkipBits(bits); + return res; + } + + private void Skip(int count) + { + this.pos += count; + this.size -= count; + this.length -= count; + if (this.size < 0) throw new NJException(NJErrorCode.SyntaxError); + } + + private void DecodeLength() + { + if (this.size < 2) throw new NJException(NJErrorCode.SyntaxError); + this.length = Decode16(this.pos); + if (this.length > this.size) throw new NJException(NJErrorCode.SyntaxError); + Skip(2); + } + + private void SkipMarker() + { + DecodeLength(); + Skip(this.length); + } + + private void DecodeDRI() + { + DecodeLength(); + if (this.length < 2) throw new NJException(NJErrorCode.SyntaxError); + this.rstinterval = Decode16(this.pos); + Skip(this.length); + } + + private static ushort Decode16(byte* pos) + { + return (ushort)((pos[0] << 8) | pos[1]); + } + + private static void FillMem(byte* block, byte value, int count) + { + for (int i = 0; i < count; i++) block[i] = value; + } + + private static void FillMem(int* block, int value, int count) + { + for (int i = 0; i < count; i++) block[i] = value; + } + + private static void FillMem(long* block, int value, int count) + { + for (int i = 0; i < count; i++) block[i] = value; + } + + private static void FillMem(Component* block, Component value, int count) + { + for (int i = 0; i < count; i++) block[i] = value; + } + + private static void FillMem(VLCCode* block, VLCCode value, int count) + { + for (int i = 0; i < count; i++) block[i] = value; + } + + #endregion + } +} \ No newline at end of file diff --git a/Ryujinx.ImGui/GUI/WindowHelper.cs b/Ryujinx.ImGui/GUI/WindowHelper.cs index b9e429e4f7..15d4d88321 100644 --- a/Ryujinx.ImGui/GUI/WindowHelper.cs +++ b/Ryujinx.ImGui/GUI/WindowHelper.cs @@ -172,7 +172,7 @@ namespace Ryujinx.UI GL.EnableClientState(ArrayCap.ColorArray); GL.Enable(EnableCap.Texture2D); - GL.UseProgram(1); + GL.UseProgram(0); // Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays) IO io = ImGui.GetIO(); @@ -194,13 +194,16 @@ namespace Ryujinx.UI GL.LoadIdentity(); // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) { NativeDrawList* cmd_list = draw_data->CmdLists[n]; byte* vtx_buffer = (byte*)cmd_list->VtxBuffer.Data; ushort* idx_buffer = (ushort*)cmd_list->IdxBuffer.Data; + DrawVert vert0 = *((DrawVert*)vtx_buffer); + DrawVert vert1 = *(((DrawVert*)vtx_buffer) + 1); + DrawVert vert2 = *(((DrawVert*)vtx_buffer) + 2); + GL.VertexPointer(2, VertexPointerType.Float, sizeof(DrawVert), new IntPtr(vtx_buffer + DrawVert.PosOffset)); GL.TexCoordPointer(2, TexCoordPointerType.Float, sizeof(DrawVert), new IntPtr(vtx_buffer + DrawVert.UVOffset)); GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(DrawVert), new IntPtr(vtx_buffer + DrawVert.ColOffset)); @@ -220,6 +223,8 @@ namespace Ryujinx.UI (int)(io.DisplaySize.Y - pcmd->ClipRect.W), (int)(pcmd->ClipRect.Z - pcmd->ClipRect.X), (int)(pcmd->ClipRect.W - pcmd->ClipRect.Y)); + ushort[] indices = new ushort[pcmd->ElemCount]; + for (int i = 0; i < indices.Length; i++) { indices[i] = idx_buffer[i]; } GL.DrawElements(PrimitiveType.Triangles, (int)pcmd->ElemCount, DrawElementsType.UnsignedShort, new IntPtr(idx_buffer)); } idx_buffer += pcmd->ElemCount; diff --git a/Ryujinx.ImGui/Ryujinx.UI.csproj b/Ryujinx.ImGui/Ryujinx.UI.csproj index 0206435cb4..28669ca7b9 100644 --- a/Ryujinx.ImGui/Ryujinx.UI.csproj +++ b/Ryujinx.ImGui/Ryujinx.UI.csproj @@ -17,6 +17,7 @@ + diff --git a/Ryujinx/Properties/launchSettings.json b/Ryujinx/Properties/launchSettings.json new file mode 100644 index 0000000000..e59dd2a51e --- /dev/null +++ b/Ryujinx/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Ryujinx": { + "commandName": "Project", + "commandLineArgs": "\"C:\\Users\\Emmanuel Hansen\\AppData\\Roaming\\RyuFs\\sdmc\\switch\\fruity\\fruity.nro\"" + } + } +} \ No newline at end of file