Add Software

This commit is contained in:
Persephone Bubblegum-Holiday 2025-03-31 18:26:04 -07:00
parent 4d0b2610d9
commit 284cf95265
22 changed files with 657 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*/*/bin/
*/*/obj/
*/bin/
*/obj/

9
LICENSE Normal file
View file

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2025 Persephone (KawaiiZenbo) Bubblegum-Holiday
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

28
PC2SSTPlayer/INIFile.cs Normal file
View file

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace SSTServoPlayer
{
public class INIFile
{
public Dictionary<string, string> Data { get; set; }
public INIFile(Dictionary<string, string> data)
{
Data = data;
}
public static INIFile Load(string inPath)
{
Dictionary<string, string> outData = new Dictionary<string, string>();
string[] rawFile = File.ReadAllLines(inPath);
foreach (string line in rawFile)
{
if (line.StartsWith(";") || line.Trim() == "") continue;
outData.Add(line.Split("=")[0].Trim(), line.Split("=")[1].Trim());
}
return new INIFile(outData);
}
}
}

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.Ports" Version="10.0.0-preview.2.25163.2" />
</ItemGroup>
</Project>

67
PC2SSTPlayer/Program.cs Normal file
View file

@ -0,0 +1,67 @@
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Text;
using System.Timers;
namespace SSTServoPlayer
{
public class Program
{
static INIFile Config;
static string PathPrefix="/";
static byte[] Signals;
static SerialPort port = null;
static System.Timers.Timer FrameTimer;
static long index = 0;
static int frameSkip = 6;
static void Main(string[] args)
{
Config = INIFile.Load(args[0]);
PathPrefix = Path.GetFullPath(args[0]).Replace("manifest.ini", "");
Signals = File.ReadAllBytes(PathPrefix+Config.Data["data"]);
port = new SerialPort(args[1], 9600, Parity.None, 8, StopBits.One);
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
port.Open();
FrameTimer = new System.Timers.Timer((1000d/double.Parse(Config.Data["frames-per-second"]))*frameSkip);
FrameTimer.Elapsed += PlaySignal;
FrameTimer.AutoReset = true;
PlayAudio();
FrameTimer.Start();
Console.ReadLine();
port.Close();
}
private static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Console.Write(port.ReadExisting());
}
static void PlayAudio()
{
Process.Start("mpv", PathPrefix+Config.Data["audio"]);
}
static void PlaySignal(Object sender, ElapsedEventArgs e)
{
if (index == Signals.Length)
{
FrameTimer.Stop();
Console.WriteLine("Complete!");
return;
}
byte b = Signals[index];
byte byte1 = (byte)(64 | ((b & 8) | (b & 4) | (b & 2) | (b & 1)));
byte byte2 = (byte)(64 | (((b & 128) | (b & 64) | (b & 32) | (b & 16)) >> 4));
port.Write(((char)byte1).ToString());
port.Write(((char)byte2).ToString());
//port.Write(((char)b).ToString());
//Console.WriteLine(byte1.ToString("B8") + " " + byte2.ToString("B8"));
index+=frameSkip;
}
}
}

View file

@ -0,0 +1,104 @@
// PINKCONNECTION2 Client Program for Helen Henny/Chuck E./Guest Star Cyberamic Servo
#include <Servo.h>
#include <string.h>
Servo servoChannel[8];
byte onDegrees[8] = { 90, 0, 0, 0, 0, 0, 0, 0 }; // these are incomplete, i need to tune them when i get the servos in
byte offDegrees[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int byte1 = 0;
int byte2 = 0;
void setup() {
servoChannel[0].attach(2);
servoChannel[1].attach(3);
servoChannel[2].attach(4);
servoChannel[3].attach(5);
servoChannel[4].attach(6);
servoChannel[5].attach(7);
servoChannel[6].attach(8);
servoChannel[7].attach(9);
pinMode(A0, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A2, OUTPUT);
pinMode(A3, OUTPUT);
pinMode(A4, OUTPUT);
pinMode(A5, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
for (int i = 0; i < 8; i++) servoChannel[i].write(offDegrees[i]);
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 1) {
byte1 = Serial.read();
byte2 = Serial.read();
if ((byte1 & 64) && (byte2 & 64))
if (byte1 & 1) {
servoChannel[0].write(onDegrees[0]);
digitalWrite(A0, 1);
} else {
servoChannel[0].write(offDegrees[0]);
digitalWrite(A0, 0);
}
if (byte1 & 2) {
servoChannel[1].write(onDegrees[1]);
digitalWrite(A1, 1);
} else {
servoChannel[1].write(offDegrees[1]);
digitalWrite(A1, 0);
}
if (byte1 & 4) {
servoChannel[2].write(onDegrees[2]);
digitalWrite(A2, 1);
} else {
servoChannel[2].write(offDegrees[2]);
digitalWrite(A2, 0);
}
if (byte1 & 8) {
servoChannel[2].write(onDegrees[3]);
digitalWrite(A3, 1);
} else {
servoChannel[2].write(offDegrees[3]);
digitalWrite(A3, 0);
}
if (byte2 & 1) {
servoChannel[4].write(onDegrees[4]);
digitalWrite(A4, 1);
} else {
servoChannel[4].write(offDegrees[4]);
digitalWrite(A4, 0);
}
if (byte2 & 2) {
servoChannel[4].write(onDegrees[5]);
digitalWrite(A5, 1);
} else {
servoChannel[4].write(offDegrees[5]);
digitalWrite(A5, 0);
}
if (byte2 & 4) {
servoChannel[6].write(onDegrees[6]);
digitalWrite(10, 1);
} else {
servoChannel[6].write(offDegrees[6]);
digitalWrite(10, 0);
}
if (byte2 & 8) {
servoChannel[7].write(onDegrees[7]);
digitalWrite(11, 1);
} else {
servoChannel[7].write(offDegrees[7]);
digitalWrite(11, 0);
}
}
}

View file

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>PinkConnection2_TestApp.Gtk</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PinkConnection2-TestApp\PinkConnection2-TestApp.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Eto.Platform.Gtk" Version="2.9.0" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,14 @@
using System;
using Eto.Forms;
namespace PinkConnection2_TestApp.Gtk
{
class Program
{
[STAThread]
public static void Main(string[] args)
{
new Application(Eto.Platforms.Gtk).Run(new MainForm());
}
}
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>PinkConnection2-TestApp</string>
<key>CFBundleIdentifier</key>
<string>me.kawaiizenbo.PinkConnection2-TestApp</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>NSHumanReadableCopyright</key>
<string></string>
<key>CFBundleIconFile</key>
<string>Icon.icns</string>
</dict>
</plist>

View file

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>PinkConnection2_TestApp.Mac</RootNamespace>
<RuntimeIdentifiers>osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PinkConnection2-TestApp\PinkConnection2-TestApp.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Eto.Platform.Mac64" Version="2.9.0" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,14 @@
using System;
using Eto.Forms;
namespace PinkConnection2_TestApp.Mac
{
class Program
{
[STAThread]
public static void Main(string[] args)
{
new Application(Eto.Platforms.Mac64).Run(new MainForm());
}
}
}

View file

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<RootNamespace>PinkConnection2_TestApp.Wpf</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PinkConnection2-TestApp\PinkConnection2-TestApp.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Eto.Platform.Wpf" Version="2.9.0" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,14 @@
using System;
using Eto.Forms;
namespace PinkConnection2_TestApp.Wpf
{
class Program
{
[STAThread]
public static void Main(string[] args)
{
new Application(Eto.Platforms.Wpf).Run(new MainForm());
}
}
}

View file

@ -0,0 +1,105 @@
using System;
using System.IO.Ports;
using Eto.Forms;
using Eto.Drawing;
namespace PinkConnection2_TestApp
{
public class MainForm : Form
{
SerialPort port = null;
byte b = 0;
TextBox CommandTextBox = new TextBox { MaxLength = 2 };
DropDown SerialPortDropDown = new DropDown();
public MainForm()
{
Title = "PinkConnection2 Test App";
Size = new Size(480, 300);
Maximizable = false;
Resizable = false;
foreach (string s in SerialPort.GetPortNames())
{
SerialPortDropDown.Items.Add(s);
}
SerialPortDropDown.SelectedValueChanged += (sender, e) => selectSerialPort();
DynamicLayout layout = new DynamicLayout { DefaultSpacing = new Size(10, 10), DefaultPadding = new Padding(5, 5, 5, 5) };
layout.BeginVertical();
layout.BeginHorizontal();
layout.Add(new Label { Text = "Serial Port" });
layout.EndHorizontal();
layout.BeginHorizontal();
layout.Add(SerialPortDropDown, true);
layout.EndHorizontal();
layout.EndVertical();
layout.BeginVertical();
layout.BeginHorizontal();
layout.Add(new Label { Text = "Channels" });
layout.EndHorizontal();
layout.BeginHorizontal();
layout.Add(new Button { Text = "Channel 1", Command = new Command((sender, e) => { if ((b&1)==1) b-=1; else b+=1; sendFrame(); }) }, true);
layout.Add(new Button { Text = "Channel 2", Command = new Command((sender, e) => { if ((b&2)==2) b-=2; else b+=2; sendFrame(); }) }, true);
layout.Add(new Button { Text = "Channel 3", Command = new Command((sender, e) => { if ((b&4)==4) b-=4; else b+=4; sendFrame(); }) }, true);
layout.Add(new Button { Text = "Channel 4", Command = new Command((sender, e) => { if ((b&8)==8) b-=8; else b+=8; sendFrame(); }) }, true);
layout.EndHorizontal();
layout.BeginHorizontal();
layout.Add(new Button { Text = "Channel 5", Command = new Command((sender, e) => { if ((b&16)==16) b-=16; else b+=16; sendFrame(); }) }, true);
layout.Add(new Button { Text = "Channel 6", Command = new Command((sender, e) => { if ((b&32)==32) b-=32; else b+=32; sendFrame(); }) }, true);
layout.Add(new Button { Text = "Channel 7", Command = new Command((sender, e) => { if ((b&64)==64) b-=64; else b+=64; sendFrame(); }) }, true);
layout.Add(new Button { Text = "Channel 8", Command = new Command((sender, e) => { if ((b&128)==128) b-=128; else b+=128; sendFrame(); }) }, true);
layout.EndHorizontal();
layout.EndVertical();
layout.BeginVertical();
layout.BeginHorizontal();
layout.Add(new Label { Text = "Send Command" });
layout.EndHorizontal();
layout.BeginHorizontal();
layout.Add(CommandTextBox, true);
layout.Add(new Button { Text = "Send", Command = new Command((sender, e) => sendRawSafe()) });
layout.EndHorizontal();
layout.EndVertical();
layout.BeginVertical();
layout.Add(null, true);
layout.EndVertical();
Content = layout;
}
void sendFrame()
{
byte byte1 = (byte)(64 | ((b & 8) | (b & 4) | (b & 2) | (b & 1)));
byte byte2 = (byte)(64 | (((b & 128) | (b & 64) | (b & 32) | (b & 16)) >> 4));
port.Write(((char)byte1).ToString());
port.Write(((char)byte2).ToString());
}
void sendRawSafe()
{
if (CommandTextBox.Text.Length != 2)
{
MessageBox.Show("Command Length must not be less than 2");
return;
}
port.Write(CommandTextBox.Text);
}
void selectSerialPort()
{
if (port != null) port.Close();
port = new SerialPort((string)SerialPortDropDown.SelectedKey, 9600, Parity.None, 8, StopBits.One);
port.Open();
}
}
}

View file

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>PinkConnection2_TestApp</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Eto.Forms" Version="2.9.0" />
<PackageReference Include="System.IO.Ports" Version="10.0.0-preview.2.25163.2" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,2 @@
cd PinkConnection2-TestApp.Gtk
dotnet build

View file

@ -0,0 +1,2 @@
cd PinkConnection2-TestApp.Mac
dotnet build

View file

@ -0,0 +1,3 @@
@ECHO OFF
cd PinkConnection2-TestApp.Wpf
dotnet build

149
rshw2sst/Program.cs Normal file
View file

@ -0,0 +1,149 @@
using System;
using System.Collections;
using System.IO;
namespace rshw2sst
{
public class Program
{
static bool usingRSHW = false;
static int charaIndex = 0;
public static void Main(string[] args)
{
Console.WriteLine("RSHW and CSHW Data Converter");
if (args[0] == "help")
{
Console.WriteLine("Usage: rshw2sst <file path> <character> <output directory>\n"
+ "This will output a SST Showtape for 1 Cyberamic Character\n"
+ "If an RSHW file is used, it will convert the bits according to Rosetta\n"
);
}
if (!File.Exists(args[0]))
{
Console.WriteLine("FATAL: Specified file does not exist.");
return;
}
switch (Path.GetExtension(args[0]).ToLower())
{
case ".rshw":
usingRSHW = true;
break;
case ".cshw":
usingRSHW = false;
break;
default:
Console.WriteLine("FATAL: Only RSHW and CSHW files are supported.");
return;
}
switch (args[1].ToLower())
{
case "chuck":
charaIndex = 0;
break;
case "helen":
charaIndex = 1;
break;
case "munch":
charaIndex = 2;
break;
case "jasper":
charaIndex = 3;
break;
case "pasqually":
charaIndex = 4;
break;
case "bella":
Console.WriteLine("you wish!!!");
return;
default:
Console.WriteLine("FATAL: Invalid character.");
return;
}
if (!Directory.Exists(args[2])) Directory.CreateDirectory(args[2]);
RSHWFile file = RSHWLoader.Load(args[0]);
if (file.signalData == null)
{
Console.WriteLine("FATAL: This file contains no signal data.");
return;
}
if (file.audioData != null) File.WriteAllBytes(args[2] + "/audio_out.wav", file.audioData);
if (file.videoData != null) File.WriteAllBytes(args[2] + "/video_out.mp4", file.videoData);
Console.WriteLine("Wrote out audio and video data.");
List<BitArray> rshwBits = new List<BitArray>();
int countlength = 0;
if (file.signalData[0] != 0)
{
countlength = 1;
BitArray bit = new BitArray(300);
rshwBits.Add(bit);
}
for (int i = 0; i < file.signalData.Length; i++)
{
if (file.signalData[i] == 0)
{
countlength += 1;
BitArray bit = new BitArray(300);
rshwBits.Add(bit);
}
else
{
rshwBits[countlength - 1].Set(file.signalData[i], true);
}
}
Console.WriteLine("Loaded RSHW signal data.");
List<byte> writeOut = new List<byte>();
long bitsOut = 0;
foreach (BitArray bits in rshwBits)
{
bitsOut++;
if (bitsOut % 6 != 0) continue;
byte frameByte = 0;
Dictionary<int, byte>[] mapping = RosettaR12;
if (usingRSHW) mapping = Rosetta3St;
foreach (KeyValuePair<int, byte> movement in mapping[charaIndex])
{
if (bits.Get(movement.Key)) frameByte += movement.Value;
}
writeOut.Add(frameByte);
}
File.WriteAllBytes(args[2] + "/signals_out.sts", writeOut.ToArray());
Console.WriteLine("Wrote out signal data.");
File.WriteAllText(args[2] + "/manifest.ini", $"; Exported by RSHW2SST\nname={Path.GetFileNameWithoutExtension(args[0])} {args[1]}\nvideo=video_out.mp4\naudio=audio_out.wav\ndata=signals_out.sts\nframes-per-second=10\nbits-per-frame=8");
Console.WriteLine("Wrote out manifest.");
if (file.videoData == null) Console.WriteLine("Warning: Video data was blank, you will need to add the file manually.");
Console.WriteLine("Complete!");
}
public static Dictionary<int, byte>[] Rosetta3St =
{
new Dictionary<int, byte>
{ {1, 1}, {8, 2}, {7, 4}, {6, 8}, {5, 16}, {4, 32}, {2, 64}, {19, 128} }, // chuck
new Dictionary<int, byte>
{ {185, 1}, {180, 2}, {179, 4}, {178, 8}, {184, 16}, {183, 32}, {181, 64}, {169, 128} }, // helen
new Dictionary<int, byte>
{ {45, 1}, {55, 2}, {54, 4}, {44, 8}, {43, 16}, {41, 32}, {60, 64}, {59, 128} }, // munch
new Dictionary<int, byte>
{ {166, 1}, {158, 2}, {157, 4}, {156, 8}, {153, 16}, {152, 32}, {151, 64}, {163, 128} }, // jasper
new Dictionary<int, byte>
{ {30, 1}, {21, 2}, {25, 4}, {29, 8}, {28, 16}, {26, 32}, {31, 64}, {35, 128} } // pasqually
};
public static Dictionary<int, byte>[] RosettaR12 =
{
new Dictionary<int, byte>
{ {1, 1}, {4, 2}, {3, 4}, {2, 8}, {5, 16}, {8, 32}, {6, 64}, {7, 128} }, // chuck
new Dictionary<int, byte>
{ {65, 1}, {68, 2}, {67, 4}, {66, 8}, {69, 16}, {72, 32}, {70, 64}, {71, 128} }, // helen
new Dictionary<int, byte>
{ {49, 1}, {51, 2}, {50, 4}, {53, 8}, {56, 16}, {54, 32}, {55, 64}, {52, 128} }, // munch
new Dictionary<int, byte>
{ {17, 1}, {20, 2}, {19, 4}, {18, 8}, {21, 16}, {24, 32}, {22, 64}, {23, 128} }, // jasper
new Dictionary<int, byte>
{ {33, 1}, {35, 2}, {34, 4}, {37, 8}, {40, 16}, {38, 32}, {39, 64}, {36, 128} } // pasqually
};
}
}

31
rshw2sst/RSHWFile.cs Normal file
View file

@ -0,0 +1,31 @@
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
sealed class AntiUnityBinder : System.Runtime.Serialization.SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
return Type.GetType(String.Format("RSHWFile, " + Assembly.GetExecutingAssembly().FullName));
}
}
[System.Serializable]
public class RSHWFile
{
public byte[]? audioData { get; set; }
public int[]? signalData { get; set; }
public byte[]? videoData { get; set; }
}
public class RSHWLoader
{
public static RSHWFile Load(string path)
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Binder = new AntiUnityBinder();
FileStream stream = File.OpenRead(path);
return (RSHWFile)formatter.Deserialize(stream);
}
}

11
rshw2sst/rshw2sst.csproj Normal file
View file

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
</Project>