227 lines
10 KiB
C#
227 lines
10 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Numerics;
|
|
using Core;
|
|
using Raylib_cs;
|
|
|
|
namespace Parsons.SteamDeck
|
|
{
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
Raylib.SetConfigFlags(ConfigFlags.ResizableWindow);
|
|
Raylib.InitWindow(1280, 800, "Parsons Master System 2026");
|
|
Raylib.InitAudioDevice();
|
|
|
|
//Raylib.SetAudioStreamBufferSizeDefault(1024);
|
|
|
|
AudioStream audioStream = Raylib.LoadAudioStream(44100, 32, 1);
|
|
Raylib.PlayAudioStream(audioStream);
|
|
|
|
SmsMachine machine = new SmsMachine();
|
|
RaylibAudioPlayer audioPlayer = new RaylibAudioPlayer();
|
|
machine.AudioProcessor.AudioDevice = audioPlayer;
|
|
|
|
Raylib.SetTargetFPS(60);
|
|
|
|
Image img = Raylib.GenImageColor(256, 192, Color.Black);
|
|
Texture2D vdpTexture = Raylib.LoadTextureFromImage(img);
|
|
byte[] pixelBuffer = new byte[256 * 192 * 4];
|
|
|
|
bool isMenuState = true;
|
|
string[] embeddedRoms = GetEmbeddedRoms();
|
|
int selectedRomIndex = 0;
|
|
bool buttonLock = false;
|
|
|
|
while (!Raylib.WindowShouldClose())
|
|
{
|
|
if (isMenuState)
|
|
{
|
|
// --- MENU INPUT LOGIC ---
|
|
if (!buttonLock)
|
|
{
|
|
if (Raylib.IsKeyDown(KeyboardKey.S) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceDown))
|
|
{
|
|
selectedRomIndex++;
|
|
if (selectedRomIndex >= embeddedRoms.Length) selectedRomIndex = 0;
|
|
buttonLock = true;
|
|
}
|
|
else if (Raylib.IsKeyDown(KeyboardKey.W) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceUp))
|
|
{
|
|
selectedRomIndex--;
|
|
if (selectedRomIndex < 0) selectedRomIndex = embeddedRoms.Length - 1;
|
|
buttonLock = true;
|
|
}
|
|
else if (Raylib.IsKeyDown(KeyboardKey.O) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceDown))
|
|
{
|
|
string targetRom = embeddedRoms[selectedRomIndex];
|
|
machine.LoadCartridge(LoadEmbeddedRomBytes(targetRom));
|
|
machine.VideoProcessor.IsGameGear = targetRom.EndsWith(".gg", StringComparison.OrdinalIgnoreCase);
|
|
isMenuState = false;
|
|
}
|
|
}
|
|
|
|
if (Raylib.IsKeyUp(KeyboardKey.S) && Raylib.IsKeyUp(KeyboardKey.W) &&
|
|
Raylib.IsGamepadButtonUp(0, GamepadButton.LeftFaceDown) && Raylib.IsGamepadButtonUp(0, GamepadButton.LeftFaceUp))
|
|
{
|
|
buttonLock = false;
|
|
}
|
|
|
|
// --- SCROLLING MENU DRAW LOGIC ---
|
|
Raylib.BeginDrawing();
|
|
Raylib.ClearBackground(new Color(20, 20, 40, 255));
|
|
|
|
Raylib.DrawText("PARSONS MASTER SYSTEM", 50, 50, 40, Color.White);
|
|
Raylib.DrawText("Select: D-Pad Start: 'A' Button Return to Menu: Select + Start", 50, 100, 20, Color.LightGray);
|
|
|
|
// THE FIX: Sliding Window Camera Math
|
|
int maxVisibleItems = 16;
|
|
int startIndex = selectedRomIndex - (maxVisibleItems / 2);
|
|
if (startIndex < 0) startIndex = 0;
|
|
if (startIndex > embeddedRoms.Length - maxVisibleItems) startIndex = Math.Max(0, embeddedRoms.Length - maxVisibleItems);
|
|
|
|
for (int i = 0; i < maxVisibleItems && (startIndex + i) < embeddedRoms.Length; i++)
|
|
{
|
|
int actualIndex = startIndex + i;
|
|
string cleanName = embeddedRoms[actualIndex].Replace("Parsons.Steamdeck.ROMS.", "");
|
|
|
|
Color textColor = (actualIndex == selectedRomIndex) ? Color.Yellow : Color.White;
|
|
string prefix = (actualIndex == selectedRomIndex) ? ">> " : " ";
|
|
|
|
Raylib.DrawText(prefix + cleanName, 80, 160 + (i * 35), 20, textColor);
|
|
}
|
|
|
|
Raylib.EndDrawing();
|
|
}
|
|
else
|
|
{
|
|
// --- EMULATOR HOTKEYS ---
|
|
|
|
// If the user holds the "Select" button (or Shift on Keyboard)
|
|
if (Raylib.IsGamepadButtonDown(0, GamepadButton.MiddleLeft) || Raylib.IsKeyDown(KeyboardKey.LeftShift))
|
|
{
|
|
// Select + Start = Return to Menu
|
|
if (Raylib.IsGamepadButtonPressed(0, GamepadButton.MiddleRight) || Raylib.IsKeyPressed(KeyboardKey.Enter))
|
|
{
|
|
isMenuState = true;
|
|
machine.Reset();
|
|
}
|
|
|
|
// Select + R1 = Save State
|
|
if (Raylib.IsGamepadButtonPressed(0, GamepadButton.RightTrigger1) || Raylib.IsKeyPressed(KeyboardKey.F5))
|
|
{
|
|
machine.SaveState("steamdeck.state");
|
|
}
|
|
|
|
// Select + L1 = Load State
|
|
if (Raylib.IsGamepadButtonPressed(0, GamepadButton.LeftTrigger1) || Raylib.IsKeyPressed(KeyboardKey.F8))
|
|
{
|
|
machine.LoadState("steamdeck.state");
|
|
}
|
|
}
|
|
|
|
// --- EMULATOR LOGIC ---
|
|
byte pad = 0xFF;
|
|
if (Raylib.IsKeyDown(KeyboardKey.Up) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceUp)) pad &= 0xFE;
|
|
if (Raylib.IsKeyDown(KeyboardKey.Down) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceDown)) pad &= 0xFD;
|
|
if (Raylib.IsKeyDown(KeyboardKey.Left) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceLeft)) pad &= 0xFB;
|
|
if (Raylib.IsKeyDown(KeyboardKey.Right) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceRight)) pad &= 0xF7;
|
|
if (Raylib.IsKeyDown(KeyboardKey.O) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceDown)) pad &= 0xEF;
|
|
if (Raylib.IsKeyDown(KeyboardKey.P) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceRight)) pad &= 0xDF;
|
|
|
|
machine.IoBus.Joypad1Keyboard = pad;
|
|
|
|
machine.RunFrame();
|
|
|
|
// C. Flush Audio Buffer to the Sound Card
|
|
// THE FIX: Do not wait for Raylib to ask! Shove the exact number of
|
|
// samples we generated this frame directly into the ring buffer.
|
|
float[] samples = audioPlayer.GetQueuedSamples();
|
|
if (samples.Length > 0)
|
|
{
|
|
unsafe
|
|
{
|
|
fixed (float* p = samples)
|
|
{
|
|
Raylib.UpdateAudioStream(audioStream, p, samples.Length);
|
|
}
|
|
}
|
|
}
|
|
|
|
//if (Raylib.IsAudioStreamProcessed(audioStream))
|
|
//{
|
|
// if (audioPlayer.AvailableSamples >= 1024)
|
|
// {
|
|
// float[] samples = audioPlayer.GetSamples(1024);
|
|
// unsafe
|
|
// {
|
|
// fixed (float* p = samples)
|
|
// {
|
|
// Raylib.UpdateAudioStream(audioStream, p, 1024);
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
int[] frameBuffer = machine.VideoProcessor.FrameBuffer;
|
|
for (int i = 0; i < frameBuffer.Length; i++)
|
|
{
|
|
int argb = frameBuffer[i];
|
|
pixelBuffer[i * 4 + 0] = (byte)((argb >> 16) & 0xFF);
|
|
pixelBuffer[i * 4 + 1] = (byte)((argb >> 8) & 0xFF);
|
|
pixelBuffer[i * 4 + 2] = (byte)(argb & 0xFF);
|
|
pixelBuffer[i * 4 + 3] = 255;
|
|
}
|
|
|
|
unsafe
|
|
{
|
|
fixed (byte* p = pixelBuffer)
|
|
{
|
|
Raylib.UpdateTexture(vdpTexture, p);
|
|
}
|
|
}
|
|
|
|
Raylib.BeginDrawing();
|
|
Raylib.ClearBackground(Color.Black);
|
|
|
|
float scale = Math.Min((float)Raylib.GetScreenWidth() / 256, (float)Raylib.GetScreenHeight() / 192);
|
|
Rectangle sourceRec = new Rectangle(0, 0, 256, 192);
|
|
Rectangle destRec = new Rectangle(
|
|
(Raylib.GetScreenWidth() - (256 * scale)) * 0.5f,
|
|
(Raylib.GetScreenHeight() - (192 * scale)) * 0.5f,
|
|
256 * scale, 192 * scale);
|
|
|
|
Raylib.DrawTexturePro(vdpTexture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White);
|
|
|
|
Raylib.EndDrawing();
|
|
}
|
|
}
|
|
|
|
Raylib.UnloadTexture(vdpTexture);
|
|
Raylib.UnloadAudioStream(audioStream);
|
|
Raylib.CloseAudioDevice();
|
|
Raylib.CloseWindow();
|
|
}
|
|
|
|
private static string[] GetEmbeddedRoms()
|
|
{
|
|
// Pulls all embedded files and filters for Master System and Game Gear
|
|
return System.Reflection.Assembly.GetExecutingAssembly()
|
|
.GetManifestResourceNames()
|
|
.Where(r => r.EndsWith(".sms", StringComparison.OrdinalIgnoreCase) ||
|
|
r.EndsWith(".gg", StringComparison.OrdinalIgnoreCase))
|
|
.ToArray();
|
|
}
|
|
|
|
private static byte[] LoadEmbeddedRomBytes(string resourceName)
|
|
{
|
|
using (Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
|
|
using (MemoryStream ms = new MemoryStream())
|
|
{
|
|
stream.CopyTo(ms);
|
|
return ms.ToArray();
|
|
}
|
|
}
|
|
}
|
|
} |