mirror of
https://gitea.tendokyu.moe/yellowberry/WACCALauncher.git
synced 2026-09-22 14:54:27 +03:00
Oops, All Rewritten!
This commit is contained in:
+4
-1
@@ -360,4 +360,7 @@ MigrationBackup/
|
|||||||
.ionide/
|
.ionide/
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# Rider
|
||||||
|
.idea/
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Rider ignored files
|
|
||||||
/modules.xml
|
|
||||||
/contentModel.xml
|
|
||||||
/.idea.WACCALauncher.iml
|
|
||||||
/projectSettingsUpdater.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="UserContentModel">
|
|
||||||
<attachedFolders />
|
|
||||||
<explicitIncludes />
|
|
||||||
<explicitExcludes />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Rider ignored files
|
|
||||||
/contentModel.xml
|
|
||||||
/.idea.WACCALauncher.iml
|
|
||||||
/modules.xml
|
|
||||||
/projectSettingsUpdater.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
-8
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="UserContentModel">
|
|
||||||
<attachedFolders />
|
|
||||||
<explicitIncludes />
|
|
||||||
<explicitExcludes />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Text;
|
||||||
|
|
||||||
|
namespace WACCALauncher
|
||||||
|
{
|
||||||
|
internal static class FontLoader
|
||||||
|
{
|
||||||
|
public static Font loadedFont;
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
|
||||||
|
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont,
|
||||||
|
IntPtr pdv, [System.Runtime.InteropServices.In] ref uint pcFonts);
|
||||||
|
|
||||||
|
private static readonly PrivateFontCollection fontCollection = new PrivateFontCollection();
|
||||||
|
|
||||||
|
public static Font LoadFont()
|
||||||
|
{
|
||||||
|
if (loadedFont != null) return loadedFont;
|
||||||
|
var fontData = Properties.Resources.menufont;
|
||||||
|
var fontPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(fontData.Length);
|
||||||
|
System.Runtime.InteropServices.Marshal.Copy(fontData, 0, fontPtr, fontData.Length);
|
||||||
|
uint dummy = 0;
|
||||||
|
fontCollection.AddMemoryFont(fontPtr, Properties.Resources.menufont.Length);
|
||||||
|
AddFontMemResourceEx(fontPtr, (uint)Properties.Resources.menufont.Length, IntPtr.Zero, ref dummy);
|
||||||
|
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(fontPtr);
|
||||||
|
|
||||||
|
loadedFont = new Font(fontCollection.Families[0], 22.5F);
|
||||||
|
return loadedFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 Yellowberry
|
Copyright (c) 2023-2025 Yellowberry
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace WACCALauncher
|
||||||
|
{
|
||||||
|
// TODO: implement themes
|
||||||
|
public enum LauncherTheme
|
||||||
|
{
|
||||||
|
Dark
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonObject]
|
||||||
|
public class LauncherSettings
|
||||||
|
{
|
||||||
|
[JsonProperty]
|
||||||
|
public string DefaultProfile { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public bool UseWatchdog { get; set; } = true;
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public LauncherTheme Theme { get; set; } = LauncherTheme.Dark;
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public bool StrictMode { get; set; } = true;
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public string ProfileDir { get; set; } = "_profiles";
|
||||||
|
|
||||||
|
public static LauncherSettings Load()
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<LauncherSettings>(File.ReadAllText("launcher.json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Save(LauncherSettings settings)
|
||||||
|
{
|
||||||
|
if (settings == null) settings = new LauncherSettings();
|
||||||
|
|
||||||
|
File.WriteAllText("launcher.json", JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+10
-17
@@ -29,7 +29,7 @@
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.menuLabel = new System.Windows.Forms.Label();
|
this.menuLabel = new System.Windows.Forms.Label();
|
||||||
this.waccaListTest = new WACCALauncher.WaccaList();
|
this.menuListBox = new WACCALauncher.WaccaList();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// menuLabel
|
// menuLabel
|
||||||
@@ -45,20 +45,13 @@
|
|||||||
this.menuLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
this.menuLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.menuLabel.Visible = false;
|
this.menuLabel.Visible = false;
|
||||||
//
|
//
|
||||||
// waccaListTest
|
// menuListBox
|
||||||
//
|
//
|
||||||
this.waccaListTest.BackColor = System.Drawing.Color.Black;
|
this.menuListBox.Enabled = false;
|
||||||
this.waccaListTest.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
this.menuListBox.FormattingEnabled = true;
|
||||||
this.waccaListTest.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
|
this.menuListBox.Name = "menuListBox";
|
||||||
this.waccaListTest.Enabled = false;
|
this.menuListBox.TabIndex = 1;
|
||||||
this.waccaListTest.ForeColor = System.Drawing.Color.White;
|
this.menuListBox.Visible = false;
|
||||||
this.waccaListTest.FormattingEnabled = true;
|
|
||||||
this.waccaListTest.ItemHeight = 40;
|
|
||||||
this.waccaListTest.Location = new System.Drawing.Point(200, 240);
|
|
||||||
this.waccaListTest.Name = "waccaListTest";
|
|
||||||
this.waccaListTest.Size = new System.Drawing.Size(680, 600);
|
|
||||||
this.waccaListTest.TabIndex = 1;
|
|
||||||
this.waccaListTest.Visible = false;
|
|
||||||
//
|
//
|
||||||
// MainForm
|
// MainForm
|
||||||
//
|
//
|
||||||
@@ -66,7 +59,7 @@
|
|||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.BackColor = System.Drawing.Color.Black;
|
this.BackColor = System.Drawing.Color.Black;
|
||||||
this.ClientSize = new System.Drawing.Size(1080, 1080);
|
this.ClientSize = new System.Drawing.Size(1080, 1080);
|
||||||
this.Controls.Add(this.waccaListTest);
|
this.Controls.Add(this.menuListBox);
|
||||||
this.Controls.Add(this.menuLabel);
|
this.Controls.Add(this.menuLabel);
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||||
this.KeyPreview = true;
|
this.KeyPreview = true;
|
||||||
@@ -75,7 +68,7 @@
|
|||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||||
this.Text = "WACCA Launcher";
|
this.Text = "WACCA Launcher";
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
|
||||||
this.Load += new System.EventHandler(this.Form1_Load);
|
this.Load += new System.EventHandler(this.MainForm_Load);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -83,7 +76,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private System.Windows.Forms.Label menuLabel;
|
private System.Windows.Forms.Label menuLabel;
|
||||||
private WaccaList waccaListTest;
|
private WaccaList menuListBox;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+521
-512
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,189 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace WACCALauncher
|
||||||
|
{
|
||||||
|
internal interface IMenuItem
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
void Select(MainForm form);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MenuSeparator : IMenuItem
|
||||||
|
{
|
||||||
|
public string Name => "";
|
||||||
|
|
||||||
|
// this should never be called
|
||||||
|
public void Select(MainForm form)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MenuAction : IMenuItem
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
private readonly Action _action;
|
||||||
|
|
||||||
|
public MenuAction(string name, Action action = null)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
_action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Select(MainForm form)
|
||||||
|
{
|
||||||
|
_action?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MenuReturn : IMenuItem
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public MenuReturn(string name = "Return")
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Select(MainForm form)
|
||||||
|
{
|
||||||
|
form._menuManager.MenuBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Menu : IMenuItem
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
public string Heading { get; }
|
||||||
|
public List<IMenuItem> Items { get; } = new List<IMenuItem>();
|
||||||
|
|
||||||
|
public bool MainMenu { get; }
|
||||||
|
|
||||||
|
public Menu(string name, string heading = null, bool main = false)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Heading = heading ?? name;
|
||||||
|
MainMenu = main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Menu(string name, List<IMenuItem> items, bool main = false)
|
||||||
|
{
|
||||||
|
Name = Heading = name;
|
||||||
|
Items = items;
|
||||||
|
MainMenu = main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IMenuItem> GetItems()
|
||||||
|
{
|
||||||
|
if(MainMenu) return Items;
|
||||||
|
|
||||||
|
var list = new List<IMenuItem>();
|
||||||
|
|
||||||
|
list.AddRange(Items);
|
||||||
|
list.Add(new MenuSeparator());
|
||||||
|
list.Add(new MenuReturn());
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Select(MainForm form)
|
||||||
|
{
|
||||||
|
form._menuManager.NavigateToSubmenu(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ProfileMenuType
|
||||||
|
{
|
||||||
|
OneTimeLaunch,
|
||||||
|
SetDefault,
|
||||||
|
Editing
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ProfileMenu : Menu
|
||||||
|
{
|
||||||
|
public ProfileMenuType MenuType { get; }
|
||||||
|
|
||||||
|
public ProfileMenu(string name, string heading = null, ProfileMenuType type = ProfileMenuType.OneTimeLaunch) : base(name, heading)
|
||||||
|
{
|
||||||
|
heading = heading ?? "Select Profile";
|
||||||
|
MenuType = type;
|
||||||
|
FillList();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void FillList()
|
||||||
|
{
|
||||||
|
if(Items.Count > 0) Items.Clear();
|
||||||
|
|
||||||
|
foreach (var profile in MainForm.Profiles)
|
||||||
|
{
|
||||||
|
Items.Add(new MenuProfileItem(profile, MenuType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MenuProfileItem : IMenuItem
|
||||||
|
{
|
||||||
|
public string Name { get => ProfileData.Name; }
|
||||||
|
|
||||||
|
public ProfileMenuType MenuType { get; }
|
||||||
|
|
||||||
|
public Profile ProfileData { get; }
|
||||||
|
|
||||||
|
public MenuProfileItem(Profile profile, ProfileMenuType type)
|
||||||
|
{
|
||||||
|
ProfileData = profile;
|
||||||
|
MenuType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Select(MainForm form)
|
||||||
|
{
|
||||||
|
// TODO: implement profile config toggle editing interface
|
||||||
|
switch(MenuType)
|
||||||
|
{
|
||||||
|
case ProfileMenuType.OneTimeLaunch:
|
||||||
|
form.SelectProfile(ProfileData);
|
||||||
|
break;
|
||||||
|
case ProfileMenuType.SetDefault:
|
||||||
|
form.settings.DefaultProfile = ProfileData.Name;
|
||||||
|
LauncherSettings.Save(form.settings);
|
||||||
|
|
||||||
|
MainForm.DefaultProfile = ProfileData;
|
||||||
|
|
||||||
|
form._menuManager.RefreshList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
switch (MenuType)
|
||||||
|
{
|
||||||
|
case ProfileMenuType.SetDefault:
|
||||||
|
return $"[{(MainForm.DefaultProfile == ProfileData ? 'X' : ' ')}] {Name}";
|
||||||
|
default:
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace WACCALauncher
|
||||||
|
{
|
||||||
|
internal class MenuManager
|
||||||
|
{
|
||||||
|
private Menu _currentMenu;
|
||||||
|
private Stack<Tuple<Menu, int>> _menuLevels = new Stack<Tuple<Menu, int>>();
|
||||||
|
private WaccaList _list;
|
||||||
|
private MainForm _form;
|
||||||
|
|
||||||
|
public MenuManager(Menu root, WaccaList list, MainForm form)
|
||||||
|
{
|
||||||
|
_currentMenu = root;
|
||||||
|
_list = list;
|
||||||
|
_list.AssignMenuManager(this);
|
||||||
|
_form = form;
|
||||||
|
UpdateList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CursorUp()
|
||||||
|
{
|
||||||
|
// move cursor up
|
||||||
|
var idx = ((_list.SelectedIndex - 1) + _list.Items.Count) % _list.Items.Count;
|
||||||
|
_list.SelectedIndex = idx;
|
||||||
|
if (_list.Items[idx] is MenuSeparator) CursorUp();
|
||||||
|
_list.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CursorDown()
|
||||||
|
{
|
||||||
|
// move cursor down
|
||||||
|
var idx = (_list.SelectedIndex + 1) % _list.Items.Count;
|
||||||
|
_list.SelectedIndex = idx;
|
||||||
|
if (_list.Items[idx] is MenuSeparator) CursorDown();
|
||||||
|
_list.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MenuBack()
|
||||||
|
{
|
||||||
|
// back from current menu item
|
||||||
|
if (_menuLevels.Count < 1)
|
||||||
|
{
|
||||||
|
_form._state = LauncherState.Launching;
|
||||||
|
_form.MenuHide();
|
||||||
|
if (MainForm.DefaultProfile == null) _form.SetDefaultProfile();
|
||||||
|
}
|
||||||
|
else NavigateBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MenuSelect()
|
||||||
|
{
|
||||||
|
// select menu item
|
||||||
|
(_list.SelectedItem as IMenuItem).Select(_form);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Menu GetCurrentMenu()
|
||||||
|
{
|
||||||
|
return _currentMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NavigateToSubmenu(Menu menu)
|
||||||
|
{
|
||||||
|
_menuLevels.Push(new Tuple<Menu, int>(_currentMenu, _list.SelectedIndex));
|
||||||
|
_currentMenu = menu;
|
||||||
|
if (menu is ProfileMenu profileMenu) profileMenu.FillList();
|
||||||
|
UpdateList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NavigateBack()
|
||||||
|
{
|
||||||
|
var back = _menuLevels.Pop();
|
||||||
|
_currentMenu = back.Item1;
|
||||||
|
UpdateList(back.Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReturnToRoot()
|
||||||
|
{
|
||||||
|
while(_menuLevels.Count > 0) NavigateBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateList(int idx = 0)
|
||||||
|
{
|
||||||
|
_list.Items.Clear();
|
||||||
|
_list.Items.AddRange(_currentMenu.GetItems().ToArray());
|
||||||
|
_form.MenuUpdateLabel(_currentMenu.Heading);
|
||||||
|
if (_list.Items.Count > 0) _list.SelectedIndex = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RefreshList()
|
||||||
|
{
|
||||||
|
_list.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace WACCALauncher
|
||||||
|
{
|
||||||
|
public enum ProfileType
|
||||||
|
{
|
||||||
|
Generic,
|
||||||
|
WACCA
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProfileLoadException : Exception
|
||||||
|
{
|
||||||
|
public ProfileLoadException(string message) : base(message) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonObject]
|
||||||
|
public class Profile
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public string ConfigPath;
|
||||||
|
|
||||||
|
[JsonProperty, JsonRequired]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty, JsonRequired]
|
||||||
|
public ProfileType Type { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty, JsonRequired]
|
||||||
|
public string BasePath;
|
||||||
|
|
||||||
|
[JsonProperty, JsonRequired]
|
||||||
|
public string GamePath;
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public string UpdaterPath;
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public string UpdaterArgs;
|
||||||
|
|
||||||
|
public DirectoryInfo GetBaseDir()
|
||||||
|
{
|
||||||
|
return new DirectoryInfo(BasePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetGamePath()
|
||||||
|
{
|
||||||
|
return Path.Combine(BasePath, GamePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for WACCA type
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public List<string> Configs { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public bool InjectGame = true;
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public string InjectDLL = "mercuryhook.dll";
|
||||||
|
|
||||||
|
public string GetAmdaemonArgs()
|
||||||
|
{
|
||||||
|
return string.Format("-f -c {0}", string.Join(" ", Configs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// static methods
|
||||||
|
|
||||||
|
public static Profile LoadFromJson(string path)
|
||||||
|
{
|
||||||
|
var loaded = JsonConvert.DeserializeObject<Profile>(File.ReadAllText(path));
|
||||||
|
loaded.ConfigPath = path;
|
||||||
|
|
||||||
|
if (!Directory.Exists(loaded.BasePath))
|
||||||
|
{
|
||||||
|
throw new ProfileLoadException("Base dir not found");
|
||||||
|
}
|
||||||
|
else if (!File.Exists(loaded.GetGamePath()))
|
||||||
|
{
|
||||||
|
throw new ProfileLoadException("Game file not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loaded.Type == ProfileType.WACCA)
|
||||||
|
{
|
||||||
|
var binPath = Path.Combine(loaded.BasePath, "bin");
|
||||||
|
if (!CheckAmdaemonConfigs(binPath, loaded.Configs))
|
||||||
|
{
|
||||||
|
throw new ProfileLoadException("configs missing, check config and files");
|
||||||
|
}
|
||||||
|
else if (!CheckForInjector(binPath, loaded.InjectDLL))
|
||||||
|
{
|
||||||
|
throw new ProfileLoadException("Segatools missing, check config and files");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveToJson()
|
||||||
|
{
|
||||||
|
File.WriteAllText(ConfigPath, JsonConvert.SerializeObject(this, Formatting.Indented));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CheckForInjector(string path, string dll)
|
||||||
|
{
|
||||||
|
return File.Exists(Path.Combine(path, dll)) &&
|
||||||
|
File.Exists(Path.Combine(path, "segatools.ini")) &&
|
||||||
|
File.Exists(Path.Combine(path, "inject.exe"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CheckAmdaemonConfigs(string path, List<string> configs)
|
||||||
|
{
|
||||||
|
int missingFiles = 0;
|
||||||
|
|
||||||
|
foreach (var cfg in configs)
|
||||||
|
{
|
||||||
|
if (!File.Exists(Path.Combine(path, cfg))) missingFiles++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return missingFiles == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
-14
@@ -1,10 +1,6 @@
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
@@ -18,21 +14,29 @@ namespace WACCALauncher
|
|||||||
|
|
||||||
public static Screen CurrentScreen = Screen.PrimaryScreen;
|
public static Screen CurrentScreen = Screen.PrimaryScreen;
|
||||||
|
|
||||||
public static bool IsCorrectRes()
|
public static bool IsCorrectRes(Screen selected = null)
|
||||||
{
|
{
|
||||||
return CurrentScreen.Bounds.Width == 1080
|
var screen = selected ?? CurrentScreen;
|
||||||
&& CurrentScreen.Bounds.Height == 1920;
|
|
||||||
|
// 1080p vertical resolution
|
||||||
|
var correctPixelRes = screen.Bounds.Width == 1080
|
||||||
|
&& screen.Bounds.Height == 1920;
|
||||||
|
|
||||||
|
// 9:16 aspect ratio check
|
||||||
|
var correctAspect = (screen.Bounds.Width / screen.Bounds.Height) == 0.5625;
|
||||||
|
|
||||||
|
return correctPixelRes || correctAspect;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsCorrectVer()
|
public static bool IsRecommendedWinVer()
|
||||||
{
|
{
|
||||||
// ensures Enterprise 2016 LTSB is used
|
// Enterprise 2016 LTSB is recommended, for various reasons
|
||||||
return BuildNumber == 14393 && ReleaseId == 1607;
|
return BuildNumber == 14393 && ReleaseId == 1607;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsCorrectEnv()
|
public static bool IsRecommendedEnv()
|
||||||
{
|
{
|
||||||
return IsCorrectRes() && IsCorrectVer();
|
return IsCorrectRes() && IsRecommendedWinVer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,9 +45,17 @@ namespace WACCALauncher
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
private static void Main()
|
private static void Main()
|
||||||
{
|
{
|
||||||
// for testing
|
// if we have multiple screens, try to start on one with proper resolution, if any
|
||||||
if(Screen.AllScreens.Length > 1)
|
if (Screen.AllScreens.Length > 1)
|
||||||
CurrentScreen = Screen.AllScreens[1];
|
{
|
||||||
|
foreach (Screen screen in Screen.AllScreens)
|
||||||
|
{
|
||||||
|
if (IsCorrectRes(screen))
|
||||||
|
{
|
||||||
|
CurrentScreen = screen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// when running as system shell, we must set our working directory manually
|
// when running as system shell, we must set our working directory manually
|
||||||
var whereAmI = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
var whereAmI = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
@@ -8,9 +7,9 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyTitle("WACCALauncher")]
|
[assembly: AssemblyTitle("WACCALauncher")]
|
||||||
[assembly: AssemblyDescription("me when i launch my wacca")]
|
[assembly: AssemblyDescription("me when i launch my wacca")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("YellowberryHN")]
|
[assembly: AssemblyCompany("Yellowberry")]
|
||||||
[assembly: AssemblyProduct("WACCALauncher")]
|
[assembly: AssemblyProduct("WACCALauncher")]
|
||||||
[assembly: AssemblyCopyright("Copyright © YellowberryHN 2024")]
|
[assembly: AssemblyCopyright("Copyright © Yellowberry 2025")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
@@ -32,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("0.11.0.0")]
|
[assembly: AssemblyVersion("0.12.0.0")]
|
||||||
[assembly: AssemblyFileVersion("0.11.0.0")]
|
[assembly: AssemblyFileVersion("0.12.0.0")]
|
||||||
|
|||||||
Generated
+20
@@ -60,6 +60,26 @@ namespace WACCALauncher.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Icon Dark {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("Dark", resourceCulture);
|
||||||
|
return ((System.Drawing.Icon)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Icon Light {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("Light", resourceCulture);
|
||||||
|
return ((System.Drawing.Icon)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Byte[].
|
/// Looks up a localized resource of type System.Byte[].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -118,6 +118,12 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="Dark" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\Dark.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
<data name="Light" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\Light.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
<data name="menufont" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="menufont" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\font.ttf;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>..\Resources\font.ttf;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
# WACCA Launcher
|
# WACCALauncher
|
||||||
|
|
||||||
Allows you to run multiple versions of WACCA from the same drive.
|
Allows you to run multiple versions of WACCA from the same drive.
|
||||||
|
Also allows you to run arbitrary programs as well.
|
||||||
|
|
||||||
## Configuration
|
## Recommended Folder Structure
|
||||||
|
|
||||||
You'll want a directory structure something like this:
|
You'll want a directory structure something like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
WACCA/
|
WACCA/
|
||||||
|
├── _profiles/
|
||||||
|
│ ├── WACCA.json
|
||||||
|
│ ├── Reverse.json
|
||||||
|
│ ├── OSD.json
|
||||||
|
│ └── ...
|
||||||
├── WACCALauncher.exe
|
├── WACCALauncher.exe
|
||||||
├── wacca.ini
|
├── launcher.json
|
||||||
|
├── ...
|
||||||
└── Versions/
|
└── Versions/
|
||||||
├── WACCA/
|
├── WACCA/
|
||||||
│ ├── bin/
|
│ ├── bin/
|
||||||
@@ -22,51 +29,116 @@ WACCA/
|
|||||||
└── Omega Supermix Deluxe
|
└── Omega Supermix Deluxe
|
||||||
```
|
```
|
||||||
|
|
||||||
Each version must have segatools already configured, with a `start.bat` file present.
|
## Profiles
|
||||||
|
|
||||||
You'll want to create a `wacca.ini` file next to the launcher that looks something like this:
|
A profile is an entry in the list of profiles displayed in the launcher.
|
||||||
|
It lets you define launch options for whatever you're launching.
|
||||||
|
|
||||||
```
|
Profiles are .json files, you can have as many profiles in your profiles folder as you want.
|
||||||
[general]
|
One limitation to be aware of is if you have a profile that shares a name with another one,
|
||||||
default_ver = reverse
|
weird things happen. But I'm not sure why you would want that anyway.
|
||||||
|
|
||||||
[versions]
|
Profiles are validated when the launcher starts, and will throw an error if the configuration is invalid,
|
||||||
wacca = C:\WACCA\Versions\WACCA
|
or if the configured files or folders do not exist.
|
||||||
wacca_s = C:\WACCA\Versions\WACCA S
|
|
||||||
lily = C:\WACCA\Versions\Lily
|
The filename of a profile is only used for sorting, so you can sort your profiles however you wish.
|
||||||
lily_r = C:\WACCA\Versions\Lily R
|
|
||||||
reverse = C:\WACCA\Versions\Reverse
|
There are currently only 2 valid types of profiles:
|
||||||
offline = C:\WACCA\Versions\Offline
|
- `WACCA`, meant for launching WACCA versions
|
||||||
|
- `Generic`, meant for launching arbitrary programs
|
||||||
|
|
||||||
|
### WACCA Profiles
|
||||||
|
|
||||||
|
A WACCA profile contains information about where key game files are, and how to launch the game.
|
||||||
|
|
||||||
|
Each WACCA profile must have segatools already configured in the `bin` folder.
|
||||||
|
|
||||||
|
Here's an example of what a WACCA Reverse profile would look like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Name": "Reverse",
|
||||||
|
"Type": "WACCA",
|
||||||
|
"BasePath": "C:\\WACCA\\Versions\\Reverse",
|
||||||
|
"GamePath": "WindowsNoEditor\\Mercury\\Binaries\\Win64\\Mercury-Win64-Shipping.exe",
|
||||||
|
"Configs": [
|
||||||
|
"config.json",
|
||||||
|
"config_lan_install_server.json",
|
||||||
|
"config_region_jpn.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Versions can be omitted, but all paths provided must be valid.
|
### Generic Profiles
|
||||||
|
|
||||||
If you have a custom version of the game you wish to launch, you may do so by adding
|
A generic profile can be used to launch pretty much any program.
|
||||||
`num_customs = 1` under `[general]`, and then adding a block at the end of the file
|
|
||||||
that looks something like this:
|
|
||||||
|
|
||||||
```
|
Here's an example of a profile you can use to launch Portal 2, for some reason:
|
||||||
[custom_1]
|
|
||||||
name = WACCA Omega Supermix Deluxe
|
```json
|
||||||
path = C:\WACCA\Versions\Omega Supermix Deluxe
|
{
|
||||||
type = reverse
|
"Name": "Portal 2 (why?)",
|
||||||
|
"Type": "Generic",
|
||||||
|
"BasePath": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Portal 2",
|
||||||
|
"GamePath": "portal2.exe"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`type` must match one of the versions listed above, it will likely be `reverse` unless specified.
|
### Updaters
|
||||||
|
|
||||||
|
Profiles have optional support for an update executable/script to be run before running the game itself.
|
||||||
|
|
||||||
|
Updaters can be added to any type of profile, and have a few specific behaviors to look out for. Updaters
|
||||||
|
are run from the directory they live in, not from the `BasePath` directory, for consistency reasons.
|
||||||
|
|
||||||
|
To add an updater to a profile, simply provide the full path to the updater in the `UpdaterPath` key.
|
||||||
|
If you need to pass arguments to the updater, you may use the `UpdaterArgs` key, but this may be omitted
|
||||||
|
if not needed.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Name": "Omega Supermix Deluxe",
|
||||||
|
"Type": "WACCA",
|
||||||
|
"BasePath": "C:\\WACCA\\Versions\\Omega Supermix Deluxe",
|
||||||
|
"GamePath": "WindowsNoEditor\\Mercury\\Binaries\\Win64\\Mercury-Win64-Shipping.exe",
|
||||||
|
"Configs": [
|
||||||
|
"config.json",
|
||||||
|
"config_lan_install_server.json",
|
||||||
|
"config_region_jpn.json"
|
||||||
|
],
|
||||||
|
"UpdaterPath": "C:\\WACCA\\Versions\\Omega Supermix Deluxe\\bin\\updater\\wupdate.exe",
|
||||||
|
"UpdaterArgs": "-t OSD"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You may skip the updater for the default profile by holding the Volume Up button while launching.
|
||||||
|
The `...` will turn into `!!!` while you are holding it.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
When you start the launcher, you will be presented with a loading prompt.
|
When you start the launcher, you will be presented with a loading prompt.
|
||||||
You may press `TEST` or <kbd>Esc</kbd> to open the configuration menu. This menu allows you
|
You may press the `TEST` button or <kbd>Esc</kbd> to open the launcher menu.
|
||||||
to change the default startup version, as well as launch a version manually.
|
This menu allows you to change the default startup profile, as well as launch a profile manually.
|
||||||
This menu is controlled in the same way you would navigate the in-game test menu.
|
It is controlled in the same way you would navigate the in-game test menu.
|
||||||
|
|
||||||
If you do not interfere with the loading prompt, the selected default version
|
You can also perform various tasks helpful for cab management, like open File Explorer, reload profiles,
|
||||||
|
or reboot your cab without a hard power cycle.
|
||||||
|
|
||||||
|
If you do not interfere with the loading prompt, the selected default profile
|
||||||
will be launched within 5 seconds.
|
will be launched within 5 seconds.
|
||||||
|
|
||||||
|
## Watchdog
|
||||||
|
|
||||||
|
By default, the process watchdog is enabled. WACCALauncher runs in the background while you are playing,
|
||||||
|
and if it detects the game closed in some way, it will clean up and attempt to open the game again,
|
||||||
|
with the same 5-second grace period as the initial launch. You may interrupt this by going into the
|
||||||
|
launcher menu, as before.
|
||||||
|
|
||||||
|
If this behavior is undesired, it can be disabled by setting `UseWatchdog` to false in `launcher.json`.
|
||||||
|
|
||||||
## Auto-launch
|
## Auto-launch
|
||||||
|
|
||||||
You can configure your cab to start the launcher instead of explorer.exe when you log in.
|
You can configure your cab to start the launcher instead of the Windows Desktop when you log in.
|
||||||
**This should only be done on a cab. You have been warned.**
|
**This should only be done on a cab. You have been warned.**
|
||||||
|
|
||||||
If you've installed the launcher in the suggested location of `C:\WACCA`,
|
If you've installed the launcher in the suggested location of `C:\WACCA`,
|
||||||
@@ -78,7 +150,9 @@ softlocked on a black screen at next boot!**
|
|||||||
|
|
||||||
## Notice
|
## Notice
|
||||||
|
|
||||||
This code was never intended to be public, as such, it is a bit of a dumpster fire.
|
This code was originally never intended to be public, as such, it is a bit of a dumpster fire.
|
||||||
|
|
||||||
|
As of version 0.12, I've cleaned it up a bit, but there's still a lot of work to be done.
|
||||||
If you want to help make the code a little less awful, issues and pull requests are welcome.
|
If you want to help make the code a little less awful, issues and pull requests are welcome.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 314 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 314 KiB |
+12
-3
@@ -37,9 +37,12 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationIcon>Resources\Dark.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="INIFileParser, Version=2.5.2.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll</HintPath>
|
<HintPath>packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SharpDX, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
<Reference Include="SharpDX, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
||||||
<HintPath>packages\SharpDX.4.2.0\lib\net45\SharpDX.dll</HintPath>
|
<HintPath>packages\SharpDX.4.2.0\lib\net45\SharpDX.dll</HintPath>
|
||||||
@@ -58,18 +61,22 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="FontLoader.cs" />
|
||||||
|
<Compile Include="LauncherSettings.cs" />
|
||||||
|
<Compile Include="Menu.cs" />
|
||||||
<Compile Include="MainForm.cs">
|
<Compile Include="MainForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="MainForm.Designer.cs">
|
<Compile Include="MainForm.Designer.cs">
|
||||||
<DependentUpon>MainForm.cs</DependentUpon>
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="MenuManager.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Profile.cs" />
|
||||||
<Compile Include="WaccaList.cs">
|
<Compile Include="WaccaList.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="WaccaVFD.cs" />
|
|
||||||
<EmbeddedResource Include="MainForm.resx">
|
<EmbeddedResource Include="MainForm.resx">
|
||||||
<DependentUpon>MainForm.cs</DependentUpon>
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
@@ -104,6 +111,8 @@
|
|||||||
<Content Include="LICENSE.txt">
|
<Content Include="LICENSE.txt">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<None Include="Resources\Light.ico" />
|
||||||
|
<None Include="Resources\Dark.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
|||||||
Binary file not shown.
+133
-12
@@ -1,11 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Data;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace WACCALauncher
|
namespace WACCALauncher
|
||||||
@@ -14,31 +8,158 @@ namespace WACCALauncher
|
|||||||
{
|
{
|
||||||
private MenuManager menuManager;
|
private MenuManager menuManager;
|
||||||
|
|
||||||
|
private Brush bgBrush;
|
||||||
|
private Brush textBrush;
|
||||||
|
private Brush highlightBrush;
|
||||||
|
|
||||||
|
private PointF[] upArrow { get; }
|
||||||
|
private PointF[] downArrow { get; }
|
||||||
|
|
||||||
public WaccaList()
|
public WaccaList()
|
||||||
{
|
{
|
||||||
|
SetStyle(
|
||||||
|
ControlStyles.OptimizedDoubleBuffer |
|
||||||
|
ControlStyles.ResizeRedraw |
|
||||||
|
ControlStyles.UserPaint,
|
||||||
|
true);
|
||||||
|
DrawMode = DrawMode.OwnerDrawFixed;
|
||||||
|
|
||||||
BackColor = Color.Black;
|
BackColor = Color.Black;
|
||||||
|
ForeColor = Color.White;
|
||||||
BorderStyle = BorderStyle.None;
|
BorderStyle = BorderStyle.None;
|
||||||
DrawMode = DrawMode.OwnerDrawVariable;
|
|
||||||
ItemHeight = 40;
|
ItemHeight = 40;
|
||||||
Size = new Size(680, 600);
|
Size = new Size(680, 600);
|
||||||
Location = new Point(200, 240);
|
Location = new Point(200, 240);
|
||||||
|
|
||||||
|
upArrow = new PointF[] {
|
||||||
|
new PointF(Bounds.Width - 30, 30),
|
||||||
|
new PointF(Bounds.Width - 20, 10),
|
||||||
|
new PointF(Bounds.Width - 10, 30)
|
||||||
|
};
|
||||||
|
|
||||||
|
downArrow = new PointF[] {
|
||||||
|
new PointF(Bounds.Width - 30, Bounds.Height - 30),
|
||||||
|
new PointF(Bounds.Width - 20, Bounds.Height - 10),
|
||||||
|
new PointF(Bounds.Width - 10, Bounds.Height - 30)
|
||||||
|
};
|
||||||
|
|
||||||
|
bgBrush = new SolidBrush(BackColor);
|
||||||
|
textBrush = new SolidBrush(ForeColor);
|
||||||
|
highlightBrush = Brushes.Red;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AssignMenuManager(MenuManager manager)
|
// hide scrollbar, we draw our own
|
||||||
|
protected override CreateParams CreateParams
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
CreateParams cp = base.CreateParams;
|
||||||
|
cp.Style = cp.Style & ~0x200000;
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("user32.dll")]
|
||||||
|
private static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
private const int LB_GETTOPINDEX = 0x018E;
|
||||||
|
|
||||||
|
private int GetTopIndex()
|
||||||
|
{
|
||||||
|
return SendMessage(Handle, LB_GETTOPINDEX, IntPtr.Zero, IntPtr.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetBottomIndex()
|
||||||
|
{
|
||||||
|
int topIdx = GetTopIndex();
|
||||||
|
|
||||||
|
int itemsVisible = ClientSize.Height / 40;
|
||||||
|
int bottomIndex = topIdx + itemsVisible - 1;
|
||||||
|
|
||||||
|
return Math.Min(bottomIndex, Items.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AtTop
|
||||||
|
{
|
||||||
|
get => GetTopIndex() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AtBottom
|
||||||
|
{
|
||||||
|
get => GetBottomIndex() == Items.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Scrollable
|
||||||
|
{
|
||||||
|
get => !(AtTop && AtBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AssignMenuManager(MenuManager manager)
|
||||||
{
|
{
|
||||||
menuManager = manager;
|
menuManager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDrawItem(DrawItemEventArgs e)
|
protected override void OnDrawItem(DrawItemEventArgs e)
|
||||||
{
|
{
|
||||||
e.Graphics.FillRectangle(new SolidBrush(BackColor), e.Bounds);
|
|
||||||
|
|
||||||
if (Items.Count < 1 || e.Index < 0) return;
|
if (Items.Count < 1 || e.Index < 0) return;
|
||||||
|
|
||||||
var text = Items[e.Index].ToString().ToUpper();
|
var item = Items[e.Index];
|
||||||
|
var bounds = e.Bounds;
|
||||||
|
|
||||||
|
e.Graphics.FillRectangle(bgBrush, bounds);
|
||||||
|
|
||||||
|
var text = item.ToString().ToUpper();
|
||||||
var selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
|
var selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
|
||||||
|
|
||||||
e.Graphics.DrawString(text, Font, selected ? Brushes.Red : Brushes.White, e.Bounds);
|
if (Scrollable)
|
||||||
|
{
|
||||||
|
bounds = new Rectangle(bounds.X, bounds.Y, bounds.Width - 40, bounds.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Graphics.DrawString(text, Font, selected ? highlightBrush : textBrush, bounds, new StringFormat() { Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap });
|
||||||
|
|
||||||
|
if (!AtTop && GetTopIndex() == e.Index)
|
||||||
|
{
|
||||||
|
e.Graphics.FillPolygon(textBrush, upArrow);
|
||||||
|
}
|
||||||
|
if (!AtBottom && GetBottomIndex() == e.Index)
|
||||||
|
{
|
||||||
|
e.Graphics.FillPolygon(textBrush, downArrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnDrawItem(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e)
|
||||||
|
{
|
||||||
|
Region iRegion = new Region(e.ClipRectangle);
|
||||||
|
e.Graphics.FillRegion(bgBrush, iRegion);
|
||||||
|
if (Items.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Items.Count; ++i)
|
||||||
|
{
|
||||||
|
Rectangle irect = GetItemRectangle(i);
|
||||||
|
if (e.ClipRectangle.IntersectsWith(irect))
|
||||||
|
{
|
||||||
|
OnDrawItem(new DrawItemEventArgs(
|
||||||
|
e.Graphics,
|
||||||
|
Font,
|
||||||
|
irect, i,
|
||||||
|
SelectedIndex == i ? DrawItemState.Selected : DrawItemState.Default,
|
||||||
|
ForeColor,
|
||||||
|
BackColor
|
||||||
|
));
|
||||||
|
iRegion.Complement(irect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnPaint(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSelectedValueChanged(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnSelectedValueChanged(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-445
@@ -1,445 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using System.IO.Ports;
|
|
||||||
using System.Security.Policy;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Drawing.Imaging;
|
|
||||||
using static System.Net.Mime.MediaTypeNames;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Data.Common;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Drawing.Drawing2D;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace WACCA
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Summary:
|
|
||||||
// Represents an ordered pair of integer x- and y-coordinates that defines a point
|
|
||||||
// in a two-dimensional plane.
|
|
||||||
[Serializable]
|
|
||||||
[TypeConverter(typeof(PointConverter))]
|
|
||||||
[ComVisible(true)]
|
|
||||||
public struct VFDPoint
|
|
||||||
{
|
|
||||||
public static readonly VFDPoint Empty;
|
|
||||||
|
|
||||||
private short x;
|
|
||||||
|
|
||||||
private byte y;
|
|
||||||
|
|
||||||
|
|
||||||
[Browsable(false)]
|
|
||||||
public bool IsEmpty
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (x == 0)
|
|
||||||
{
|
|
||||||
return y == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public short X
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
x = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte Y
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
y = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VFDPoint(short x, byte y)
|
|
||||||
{
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VFDPoint(Size sz)
|
|
||||||
{
|
|
||||||
x = (short)sz.Width;
|
|
||||||
y = (byte)sz.Height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static explicit operator Size(VFDPoint p)
|
|
||||||
{
|
|
||||||
return new Size(p.X, p.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VFDPoint operator +(VFDPoint pt, Size sz)
|
|
||||||
{
|
|
||||||
return Add(pt, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VFDPoint operator -(VFDPoint pt, Size sz)
|
|
||||||
{
|
|
||||||
return Subtract(pt, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(VFDPoint left, VFDPoint right)
|
|
||||||
{
|
|
||||||
if (left.X == right.X)
|
|
||||||
{
|
|
||||||
return left.Y == right.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(VFDPoint left, VFDPoint right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VFDPoint Add(VFDPoint pt, Size sz)
|
|
||||||
{
|
|
||||||
return new VFDPoint((short)(pt.X + sz.Width), (byte)(pt.Y + sz.Height));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VFDPoint Subtract(VFDPoint pt, Size sz)
|
|
||||||
{
|
|
||||||
return new VFDPoint((short)(pt.X - sz.Width), (byte)(pt.Y - sz.Height));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (!(obj is VFDPoint))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
VFDPoint point = (VFDPoint)obj;
|
|
||||||
if (point.X == X)
|
|
||||||
{
|
|
||||||
return point.Y == Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return x ^ y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Offset(short dx, byte dy)
|
|
||||||
{
|
|
||||||
X += dx;
|
|
||||||
Y += dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Offset(VFDPoint p)
|
|
||||||
{
|
|
||||||
Offset(p.X, p.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "{X=" + X.ToString(CultureInfo.CurrentCulture) + ",Y=" + Y.ToString(CultureInfo.CurrentCulture) + "}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VFD
|
|
||||||
{
|
|
||||||
SerialPort port;
|
|
||||||
public Lang language { get; private set; } = Lang.SIMP_CHINESE;
|
|
||||||
public Font font { get; private set; } = Font._16_16;
|
|
||||||
public Bright brightness { get; private set; } = Bright._100;
|
|
||||||
public bool power { get; private set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Establish a connection to a VFD, and prepare it for use.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="portName">The port that the VFD connected to</param>
|
|
||||||
public VFD(string portName = "COM2")
|
|
||||||
{
|
|
||||||
port = new SerialPort(portName, 115200);
|
|
||||||
port.Open();
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void VFD_Write(byte number)
|
|
||||||
{
|
|
||||||
Console.WriteLine(BitConverter.ToString(new byte[] { number }));
|
|
||||||
port.Write(new byte[] { number }, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void VFD_Write(byte[] bytes)
|
|
||||||
{
|
|
||||||
Console.WriteLine(BitConverter.ToString(bytes));
|
|
||||||
port.Write(bytes, 0, bytes.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void VFD_Write(string text)
|
|
||||||
{
|
|
||||||
// Get correct encoding for current language
|
|
||||||
int codeNumber;
|
|
||||||
switch(language)
|
|
||||||
{
|
|
||||||
case Lang.SIMP_CHINESE:
|
|
||||||
codeNumber = 936; // GB2312
|
|
||||||
break;
|
|
||||||
case Lang.TRAD_CHINESE:
|
|
||||||
codeNumber = 950; // Big5
|
|
||||||
break;
|
|
||||||
case Lang.JAPANESE:
|
|
||||||
codeNumber = 932; // Shift-JIS
|
|
||||||
break;
|
|
||||||
case Lang.KOREAN:
|
|
||||||
codeNumber = 949; // KSC5601
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
codeNumber = 932;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert Unicode string to encoded bytes
|
|
||||||
Encoding unicodeEncoding = Encoding.Unicode;
|
|
||||||
Encoding correctEncoding = Encoding.GetEncoding(codeNumber);
|
|
||||||
byte[] unicodeBytes = unicodeEncoding.GetBytes(text);
|
|
||||||
byte[] encodedBytes = Encoding.Convert(unicodeEncoding, correctEncoding, unicodeBytes);
|
|
||||||
|
|
||||||
Console.WriteLine(BitConverter.ToString(correctEncoding.GetBytes(text)));
|
|
||||||
port.Write(encodedBytes, 0, encodedBytes.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define LEFT_HI(x) (((x) & 0x100) >> 8)
|
|
||||||
#define LEFT_LO(x) ((x) & 0xFF)
|
|
||||||
|
|
||||||
#define FTB_PORT_WRITE_LEFT(x) {FTB_PORT.write(LEFT_HI(x)); FTB_PORT.write(LEFT_LO(x));}
|
|
||||||
*/
|
|
||||||
|
|
||||||
private void VFD_WriteShort(short x)
|
|
||||||
{
|
|
||||||
byte hi = (byte)(((x) & 0x100) >> 8);
|
|
||||||
byte lo = (byte)((x) & 0xFF);
|
|
||||||
VFD_Write(new byte[] {hi, lo});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write(string text)
|
|
||||||
{
|
|
||||||
VFD_Write(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x0B });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x0C });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TestPayload() // fucked
|
|
||||||
{
|
|
||||||
VFD_Write(Encoding.ASCII.GetString(new byte[] { 0x1b, 0x0c, 0x1b, 0x52, 0x1b, 0x40, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x02, 0x1b, 0x41, 0x00, 0x1b, 0x50, 0x50, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x1b, 0x51}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Bright {
|
|
||||||
_0 = 0,
|
|
||||||
_25 = 1,
|
|
||||||
_50 = 2,
|
|
||||||
_75 = 3,
|
|
||||||
_100 = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Brightness(Bright brightness)
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x20, (byte)brightness });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PowerOn()
|
|
||||||
{
|
|
||||||
Power(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PowerOff()
|
|
||||||
{
|
|
||||||
Power(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Power(bool on)
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x21, (byte)(on ? 0x01 : 0x00) });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CanvasShift(short left)
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x22 });
|
|
||||||
VFD_WriteShort(left);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cursor(short left, byte top)
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x30 });
|
|
||||||
VFD_WriteShort(left);
|
|
||||||
VFD_Write(top);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Lang {
|
|
||||||
SIMP_CHINESE,
|
|
||||||
TRAD_CHINESE,
|
|
||||||
JAPANESE,
|
|
||||||
KOREAN
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Language(Lang lang)
|
|
||||||
{
|
|
||||||
language = lang;
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x32, (byte)language });
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Font
|
|
||||||
{
|
|
||||||
_16_16,
|
|
||||||
_6_8
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FontSize(Font size)
|
|
||||||
{
|
|
||||||
// 3
|
|
||||||
font = size;
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x33, (byte)size });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateScrollBox(short left, byte top, short width, byte height)
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x40 });
|
|
||||||
VFD_WriteShort(left);
|
|
||||||
VFD_Write(top);
|
|
||||||
VFD_WriteShort(width);
|
|
||||||
VFD_Write(height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ScrollSpeed(byte divisor)
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x41, (byte)divisor });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ScrollText(string text)
|
|
||||||
{
|
|
||||||
if (text.Length >= 0x100) throw new ArgumentOutOfRangeException("Text is too long.");
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x50, (byte)text.Length });
|
|
||||||
VFD_Write(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ScrollStart()
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x51 });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ScrollStop()
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x52 });
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum BlinkMode
|
|
||||||
{
|
|
||||||
Off = 0,
|
|
||||||
Invert = 1,
|
|
||||||
All = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BlinkSet(BlinkMode blink, byte interval)
|
|
||||||
{
|
|
||||||
VFD_Write(new byte[] { 0x1B, 0x23, (byte)blink, interval });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearLine(byte line)
|
|
||||||
{
|
|
||||||
Cursor(0, line);
|
|
||||||
VFD_Write("".PadLeft(20));
|
|
||||||
Cursor(0, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawBitmap(Bitmap bmp, Point origin)
|
|
||||||
{
|
|
||||||
if (bmp.PixelFormat != PixelFormat.Format1bppIndexed)
|
|
||||||
throw new ArgumentException("Provided bitmap is not monochrome");
|
|
||||||
|
|
||||||
// We have to do it this way because of a GDI+ bug
|
|
||||||
bmp.RotateFlip(RotateFlipType.Rotate270FlipNone);
|
|
||||||
RotateNoneFlipYMono(bmp);
|
|
||||||
|
|
||||||
Rectangle bounds = new Rectangle(new Point(), bmp.Size);
|
|
||||||
|
|
||||||
var data = bmp.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
|
|
||||||
|
|
||||||
VFD_Write("\x1B\x2E");
|
|
||||||
VFD_WriteShort((short)origin.X);
|
|
||||||
VFD_Write((byte)origin.Y);
|
|
||||||
VFD_WriteShort((short)bmp.Height); // Inverted because image was flipped
|
|
||||||
VFD_Write((byte)((bmp.Width / 8)-1));
|
|
||||||
|
|
||||||
int bytes = ( bmp.Width * bmp.Height ) / 8;
|
|
||||||
|
|
||||||
// Create a byte array to hold the pixel data
|
|
||||||
byte[] pixelData = new byte[bytes];
|
|
||||||
|
|
||||||
// Copy the data from the pointer to the byte array
|
|
||||||
Marshal.Copy(data.Scan0, pixelData, 0, bytes);
|
|
||||||
|
|
||||||
VFD_Write(pixelData);
|
|
||||||
|
|
||||||
bmp.UnlockBits(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RotateNoneFlipYMono(Bitmap bmp)
|
|
||||||
{
|
|
||||||
if (bmp == null || bmp.PixelFormat != PixelFormat.Format1bppIndexed)
|
|
||||||
throw new ArgumentException("Provided bitmap is not monochrome");
|
|
||||||
|
|
||||||
var height = bmp.Height;
|
|
||||||
var width = bmp.Width;
|
|
||||||
// width in dwords
|
|
||||||
var stride = (width + 31) >> 5;
|
|
||||||
// total image size
|
|
||||||
var size = stride * height;
|
|
||||||
// alloc storage for pixels
|
|
||||||
var bytes = new int[size];
|
|
||||||
|
|
||||||
// get image pixels
|
|
||||||
var rect = new Rectangle(Point.Empty, bmp.Size);
|
|
||||||
var bd = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
|
|
||||||
Marshal.Copy(bd.Scan0, bytes, 0, size);
|
|
||||||
|
|
||||||
// flip by swapping dwords
|
|
||||||
int halfSize = size >> 1;
|
|
||||||
for (int y1 = 0, y2 = size - stride; y1 < halfSize; y1 += stride, y2 -= stride)
|
|
||||||
{
|
|
||||||
int end = y1 + stride;
|
|
||||||
for (int x1 = y1, x2 = y2; x1 < end; x1++, x2++)
|
|
||||||
{
|
|
||||||
bytes[x1] ^= bytes[x2];
|
|
||||||
bytes[x2] ^= bytes[x1];
|
|
||||||
bytes[x1] ^= bytes[x2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy pixels back
|
|
||||||
Marshal.Copy(bytes, 0, bd.Scan0, size);
|
|
||||||
bmp.UnlockBits(bd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-1
@@ -1,3 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/></startup></configuration>
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="ini-parser" version="2.5.2" targetFramework="net452" />
|
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net452" />
|
||||||
<package id="SharpDX" version="4.2.0" targetFramework="net452" />
|
<package id="SharpDX" version="4.2.0" targetFramework="net452" />
|
||||||
<package id="SharpDX.DirectInput" version="4.2.0" targetFramework="net452" />
|
<package id="SharpDX.DirectInput" version="4.2.0" targetFramework="net452" />
|
||||||
</packages>
|
</packages>
|
||||||
Reference in New Issue
Block a user