125 lines
5.5 KiB
C#
125 lines
5.5 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)
|
|
{
|
|
// 1. Initialize Cross-Platform OpenGL Window
|
|
// THE FIX: Use PascalCase C# enums
|
|
Raylib.SetConfigFlags(ConfigFlags.ResizableWindow);
|
|
Raylib.InitWindow(1280, 800, "Parsons Master System"); // Native Steam Deck Resolution
|
|
Raylib.InitAudioDevice();
|
|
|
|
// 2. Initialize Hardware-Agnostic Audio Stream (44.1kHz, 32-bit float, Mono)
|
|
AudioStream audioStream = Raylib.LoadAudioStream(44100, 32, 1);
|
|
Raylib.PlayAudioStream(audioStream);
|
|
|
|
// 3. Boot the Core Emulator
|
|
SmsMachine machine = new SmsMachine();
|
|
RaylibAudioPlayer audioPlayer = new RaylibAudioPlayer();
|
|
machine.AudioProcessor.AudioDevice = audioPlayer;
|
|
|
|
// Load a ROM (Passed via command line argument, e.g. ./Parsons.SteamDeck sonic.sms)
|
|
if (args.Length > 0 && File.Exists(args[0]))
|
|
{
|
|
machine.LoadCartridge(File.ReadAllBytes(args[0]));
|
|
bool isGG = args[0].EndsWith(".gg", StringComparison.OrdinalIgnoreCase);
|
|
machine.VideoProcessor.IsGameGear = isGG;
|
|
}
|
|
|
|
// Lock the engine loop to 60 FPS natively
|
|
Raylib.SetTargetFPS(60);
|
|
|
|
// 4. Prepare the Video Texture Pipeline
|
|
// THE FIX: Use Color.Black
|
|
Image img = Raylib.GenImageColor(256, 192, Color.Black);
|
|
Texture2D vdpTexture = Raylib.LoadTextureFromImage(img);
|
|
byte[] pixelBuffer = new byte[256 * 192 * 4]; // Fast RGBA translation array
|
|
|
|
// --- THE MAIN GAME LOOP ---
|
|
while (!Raylib.WindowShouldClose())
|
|
{
|
|
// A. OS-Agnostic Input Polling (Works on Keyboard AND Steam Deck Controller)
|
|
// THE FIX: PascalCase KeyboardKey and GamepadButton enums
|
|
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;
|
|
|
|
// Steam Deck "A" Button is RightFaceDown. "B" Button is RightFaceRight.
|
|
if (Raylib.IsKeyDown(KeyboardKey.Z) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceDown)) pad &= 0xEF; // B1 / A Button
|
|
if (Raylib.IsKeyDown(KeyboardKey.X) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceRight)) pad &= 0xDF; // B2 / B Button
|
|
|
|
machine.IoBus.Joypad1Keyboard = pad;
|
|
|
|
// B. Execute exactly one frame of T-States
|
|
machine.RunFrame();
|
|
|
|
// C. Flush Audio Buffer to the Sound Card
|
|
if (Raylib.IsAudioStreamProcessed(audioStream))
|
|
{
|
|
float[] samples = audioPlayer.GetQueuedSamples();
|
|
if (samples.Length > 0)
|
|
{
|
|
unsafe
|
|
{
|
|
fixed (float* p = samples)
|
|
{
|
|
Raylib.UpdateAudioStream(audioStream, p, samples.Length);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// D. Fast Pixel Translation (ARGB int -> RGBA bytes)
|
|
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); // R
|
|
pixelBuffer[i * 4 + 1] = (byte)((argb >> 8) & 0xFF); // G
|
|
pixelBuffer[i * 4 + 2] = (byte)(argb & 0xFF); // B
|
|
pixelBuffer[i * 4 + 3] = 255; // A
|
|
}
|
|
|
|
// Push bytes directly to the GPU Texture
|
|
unsafe
|
|
{
|
|
fixed (byte* p = pixelBuffer)
|
|
{
|
|
Raylib.UpdateTexture(vdpTexture, p);
|
|
}
|
|
}
|
|
|
|
// E. Render to Screen (Perfect Aspect Ratio Scaling)
|
|
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);
|
|
|
|
// Draw the VDP Frame
|
|
Raylib.DrawTexturePro(vdpTexture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White);
|
|
|
|
Raylib.EndDrawing();
|
|
}
|
|
|
|
// Clean up hardware resources on exit
|
|
Raylib.UnloadTexture(vdpTexture);
|
|
Raylib.UnloadAudioStream(audioStream);
|
|
Raylib.CloseAudioDevice();
|
|
Raylib.CloseWindow();
|
|
}
|
|
}
|
|
} |