Add one-time version selection, VFD util updates, added launch option for offline version.

This commit is contained in:
Zsolt Zitting 2023-10-23 03:35:10 -07:00
parent 0400ee4472
commit 5da9d62616
2 changed files with 405 additions and 55 deletions

View File

@ -12,6 +12,7 @@ using IniParser.Model;
using SharpDX.DirectInput;
using System.Linq;
using System.Reflection;
using WACCA;
namespace WACCALauncher
{
@ -43,6 +44,7 @@ namespace WACCALauncher
private bool _gameRunning = false;
public MenuManager _menuManager;
private VFD _vfd;
public MainForm()
{
@ -218,15 +220,16 @@ namespace WACCALauncher
private static void vfd_test()
{
var vfd = new WaccaVFD();
vfd.Power(true);
vfd.Clear();
vfd.Brightness(WaccaVFD.bright.BRIGHT_50);
vfd.Cursor(0, 0);
var vfd = new VFD();
vfd.Reset();
vfd.PowerOn();
vfd.Brightness(VFD.Bright._50);
vfd.CanvasShift(0);
vfd.Cursor(0, 0);
vfd.FontSize(VFD.Font._16_16);
vfd.Write("Testing VFD!");
vfd.Cursor(0, 16);
vfd.ScrollSpeed(2);
vfd.Cursor(0, 2);
vfd.ScrollSpeed(15);
vfd.ScrollText(Math.PI.ToString() + " ");
vfd.ScrollStart();
}
@ -266,8 +269,8 @@ namespace WACCALauncher
LoadVersionsFromConfig();
var mainMenu = new ConfigMenu("Launcher Settings", items: new List<ConfigMenu>() {
new ConfigMenu("one-time launch", ConfigMenuAction.Menu, items: GetOTLMenu()),
new ConfigMenu("set default version", ConfigMenuAction.Menu, items: GetDefaultVersionMenu()),
new ConfigMenu("test VFD", ConfigMenuAction.Command, method: vfd_test),
new ConfigMenu("exit to windows", ConfigMenuAction.Command, method: Application.Exit),
new ConfigMenu("launch game", ConfigMenuAction.Return)
});
@ -285,7 +288,7 @@ namespace WACCALauncher
foreach (var ver in Versions)
{
var name = ver.GameVersion == VersionType.Custom ? ver.CustomName : ver.ToString();
defVerMenu.Add(new ConfigMenu($"({(ver == DefaultVer ? 'X' : ' ')}) {name}", ConfigMenuAction.VersionSelect, version: ver));
defVerMenu.Add(new ConfigMenu($"({(ver == DefaultVer ? 'X' : ' ')}) {name}", ConfigMenuAction.VersionSelect, version: ver, defVer: true));
}
defVerMenu.Add(new ConfigMenu("Return to settings", ConfigMenuAction.Return));
@ -293,6 +296,21 @@ namespace WACCALauncher
return defVerMenu;
}
public List<ConfigMenu> GetOTLMenu()
{
var otlMenu = new List<ConfigMenu>();
foreach (var ver in Versions)
{
var name = ver.GameVersion == VersionType.Custom ? ver.CustomName : ver.ToString();
otlMenu.Add(new ConfigMenu(name, ConfigMenuAction.VersionSelect, version: ver, defVer: false));
}
otlMenu.Add(new ConfigMenu("Return to settings", ConfigMenuAction.Return));
return otlMenu;
}
private static void KillExplorer()
{
Process.Start(@"C:\Windows\System32\taskkill.exe", @"/F /IM explorer.exe");
@ -304,7 +322,7 @@ namespace WACCALauncher
if (processes.Length == 0) Process.Start("explorer.exe");
}
private void LaunchGame(Version version)
public void LaunchGame(Version version)
{
Console.WriteLine("launching game");
_gameProcess.StartInfo.FileName = version.BatchPath;
@ -438,6 +456,11 @@ namespace WACCALauncher
Controls.Add(errorLabel);
}
public void StopTimer()
{
_delayTimer.Stop();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
OpenExplorer();
@ -452,6 +475,7 @@ namespace WACCALauncher
Lily,
Lily_R,
Reverse,
Offline,
Custom = 10
}
@ -516,6 +540,7 @@ namespace WACCALauncher
private readonly Action _method;
private readonly List<string> _options;
private readonly Version _version;
private readonly bool _defVer;
public void Select(MainForm form)
{
@ -535,15 +560,26 @@ namespace WACCALauncher
}
else if (_action == ConfigMenuAction.VersionSelect && _version != null)
{
Console.WriteLine($"setting default version to {_version}");
form.SetDefaultVer(_version);
// TODO: this is kinda jank, fix this
form._menuManager.UpdateCurrentMenuItems(form.GetDefaultVersionMenu());
if(_defVer)
{
Console.WriteLine($"setting default version to {_version}");
form.SetDefaultVer(_version);
// TODO: this is kinda jank, fix this
form._menuManager.UpdateCurrentMenuItems(form.GetDefaultVersionMenu());
}
else
{
Console.WriteLine($"one-time launch for {_version}");
form.MenuHide();
form.StopTimer();
form.LaunchGame(_version);
}
}
else if (_action == ConfigMenuAction.Return) { form._menuManager.MenuBack(); }
}
public ConfigMenu(string name, ConfigMenuAction action = ConfigMenuAction.None, Action method = null, List<ConfigMenu> items = null, List<string> options = null, Version version = null)
public ConfigMenu(string name, ConfigMenuAction action = ConfigMenuAction.None, Action method = null, List<ConfigMenu> items = null, List<string> options = null, Version version = null, bool defVer = false)
{
this.Name = name;
this._action = action;
@ -561,6 +597,7 @@ namespace WACCALauncher
this._method = method;
this._options = options;
this._version = version;
this._defVer = defVer;
}
public override string ToString()

View File

@ -1,36 +1,239 @@
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 WACCALauncher
namespace WACCA
{
class WaccaVFD
//
// 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;
public WaccaVFD(string portName = "COM2")
/// <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")
{
this.port = new SerialPort(portName, 115200);
port = new SerialPort(portName, 115200);
port.Open();
Reset();
}
private void VFD_Write(byte number)
{
VFD_Write($"{(char)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)
{
Console.WriteLine(BitConverter.ToString(Encoding.Default.GetBytes(text)));
port.Write(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)
{
char hi = (char)((x & 0x100) >> 8);
char lo = (char)(x & 0xFF);
VFD_Write($"{hi}{lo}");
byte hi = (byte)(((x) & 0x100) >> 8);
byte lo = (byte)((x) & 0xFF);
VFD_Write(new byte[] {hi, lo});
}
public void Write(string text)
@ -40,71 +243,89 @@ namespace WACCALauncher
public void Reset()
{
VFD_Write("\x1B\x0B");
VFD_Write(new byte[] { 0x1B, 0x0B });
}
public void Clear()
{
VFD_Write("\x1B\x0C");
VFD_Write(new byte[] { 0x1B, 0x0C });
}
public enum bright {
BRIGHT_0 = 0,
BRIGHT_25 = 1,
BRIGHT_50 = 2,
BRIGHT_75 = 3,
BRIGHT_100 = 4
}
public void Brightness(bright brightness)
public void TestPayload() // fucked
{
VFD_Write("\x1B\x20" + (char)brightness);
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 void Power(bool on)
public enum Bright {
_0 = 0,
_25 = 1,
_50 = 2,
_75 = 3,
_100 = 4
}
public void Brightness(Bright brightness)
{
VFD_Write("\x1B\x21" + (on ? "\x01" : "\x00"));
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("\x1B\x22");
VFD_Write(new byte[] { 0x1B, 0x22 });
VFD_WriteShort(left);
}
public void Cursor(short left, byte top)
{
VFD_Write("\x1B\x30");
VFD_Write(new byte[] { 0x1B, 0x30 });
VFD_WriteShort(left);
VFD_Write(top);
}
public enum lang {
public enum Lang {
SIMP_CHINESE,
TRAD_CHINESE,
JAPANESE,
KOREAN
}
public void Language(lang language)
public void Language(Lang lang)
{
VFD_Write("\x1B\x32" + (char)language);
language = lang;
VFD_Write(new byte[] { 0x1B, 0x32, (byte)language });
}
public enum font_size
public enum Font
{
FONT_16_16,
FONT_6_8
_16_16,
_6_8
}
public void FontSize(font_size size)
public void FontSize(Font size)
{
VFD_Write("\x1B\x33" + (char)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("\x1B\x40");
VFD_Write(new byte[] { 0x1B, 0x40 });
VFD_WriteShort(left);
VFD_Write(top);
VFD_WriteShort(width);
@ -113,20 +334,112 @@ namespace WACCALauncher
public void ScrollSpeed(byte divisor)
{
VFD_Write("\x1B\x33" + (char)divisor);
VFD_Write(new byte[] { 0x1B, 0x41, (byte)divisor });
}
public void ScrollText(string text)
{
if (text.Length > 255) throw new ArgumentOutOfRangeException("Text is too long.");
VFD_Write("\x1B\x50");
VFD_Write((byte)text.Length);
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("\x1B\x51");
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);
}
}
}