This commit is contained in:
Jan Racek
2026-03-27 18:31:35 +01:00
parent 1c48b8b425
commit 24d0c1fa07
27 changed files with 779 additions and 609 deletions

View File

@@ -60,6 +60,7 @@ namespace SplashEdit.EditorCode
private const int FUNC_SEEK = 0x107;
public bool IsRunning => _listenTask != null && !_listenTask.IsCompleted;
public bool HasError { get; private set; }
public PCdrvSerialHost(string portName, int baudRate, string baseDir, Action<string> log, Action<string> psxLog = null)
{
@@ -157,6 +158,7 @@ namespace SplashEdit.EditorCode
bool lastByteWasEscape = false;
var textBuffer = new StringBuilder();
int totalBytesReceived = 0;
int consecutiveErrors = 0;
DateTime lastLogTime = DateTime.Now;
_log?.Invoke("PCdrv monitor: waiting for data from PS1...");
@@ -179,6 +181,7 @@ namespace SplashEdit.EditorCode
}
int b = _port.ReadByte();
consecutiveErrors = 0;
totalBytesReceived++;
// Log first bytes received to help diagnose protocol issues
@@ -256,8 +259,16 @@ namespace SplashEdit.EditorCode
catch (OperationCanceledException) { break; }
catch (Exception ex)
{
if (!ct.IsCancellationRequested)
_log?.Invoke($"PCdrv monitor error: {ex.Message}");
if (ct.IsCancellationRequested) break;
consecutiveErrors++;
_log?.Invoke($"PCdrv monitor error: {ex.Message}");
if (consecutiveErrors >= 3)
{
_log?.Invoke("PCdrv host: too many errors, connection lost. Stopping.");
HasError = true;
break;
}
Thread.Sleep(100); // Back off before retry
}
}
}

View File

@@ -247,9 +247,25 @@ namespace SplashEdit.EditorCode
return tex;
}
// Snapshot taken at the start of each OnGUI so Layout and Repaint
// events always see the same line count (prevents "Getting control
// position in a group with only N controls" errors).
private LogLine[] _snapshot = Array.Empty<LogLine>();
private void OnGUI()
{
EnsureStyles();
// Take a snapshot once per OnGUI so Layout and Repaint see
// identical control counts even if background threads add lines.
if (Event.current.type == EventType.Layout)
{
lock (_lock)
{
_snapshot = _lines.ToArray();
}
}
DrawToolbar();
DrawConsoleOutput();
}
@@ -310,69 +326,70 @@ namespace SplashEdit.EditorCode
int selMax = Mathf.Max(_selectionAnchor, _selectionEnd);
bool hasSelection = _selectionAnchor >= 0 && _selectionEnd >= 0;
lock (_lock)
// Iterate the snapshot taken during Layout so the control count
// is stable across Layout and Repaint events.
var snapshot = _snapshot;
if (snapshot.Length == 0)
{
if (_lines.Count == 0)
GUILayout.Label("Waiting for output...", EditorStyles.centeredGreyMiniLabel);
}
for (int i = 0; i < snapshot.Length; i++)
{
var line = snapshot[i];
if (line.isError && !_showStderr) continue;
if (!line.isError && !_showStdout) continue;
if (hasFilter && line.text.ToLowerInvariant().IndexOf(filterLower, StringComparison.Ordinal) < 0)
continue;
bool selected = hasSelection && i >= selMin && i <= selMax;
GUIStyle style = selected ? _monoStyleSelected : (line.isError ? _monoStyleErr : _monoStyle);
string label = $"[{line.timestamp}] {line.text}";
GUILayout.Label(label, style);
// Handle click/right-click on last drawn rect
Rect lineRect = GUILayoutUtility.GetLastRect();
Event evt = Event.current;
if (evt.type == EventType.MouseDown && lineRect.Contains(evt.mousePosition))
{
GUILayout.Label("Waiting for output...", EditorStyles.centeredGreyMiniLabel);
}
for (int i = 0; i < _lines.Count; i++)
{
var line = _lines[i];
if (line.isError && !_showStderr) continue;
if (!line.isError && !_showStdout) continue;
if (hasFilter && line.text.ToLowerInvariant().IndexOf(filterLower, StringComparison.Ordinal) < 0)
continue;
bool selected = hasSelection && i >= selMin && i <= selMax;
GUIStyle style = selected ? _monoStyleSelected : (line.isError ? _monoStyleErr : _monoStyle);
string label = $"[{line.timestamp}] {line.text}";
GUILayout.Label(label, style);
// Handle click/right-click on last drawn rect
Rect lineRect = GUILayoutUtility.GetLastRect();
Event evt = Event.current;
if (evt.type == EventType.MouseDown && lineRect.Contains(evt.mousePosition))
if (evt.button == 0)
{
if (evt.button == 0)
if (evt.shift && _selectionAnchor >= 0)
_selectionEnd = i;
else
{
if (evt.shift && _selectionAnchor >= 0)
_selectionEnd = i;
else
{
_selectionAnchor = i;
_selectionEnd = i;
}
evt.Use();
Repaint();
_selectionAnchor = i;
_selectionEnd = i;
}
else if (evt.button == 1)
evt.Use();
Repaint();
}
else if (evt.button == 1)
{
int clickedLine = i;
bool lineInSelection = hasSelection && clickedLine >= selMin && clickedLine <= selMax;
var menu = new GenericMenu();
if (lineInSelection && selMin != selMax)
{
int clickedLine = i;
bool lineInSelection = hasSelection && clickedLine >= selMin && clickedLine <= selMax;
var menu = new GenericMenu();
if (lineInSelection && selMin != selMax)
{
menu.AddItem(new GUIContent("Copy selected lines"), false, () => CopyRange(selMin, selMax));
menu.AddSeparator("");
}
menu.AddItem(new GUIContent("Copy this line"), false, () =>
{
string text;
lock (_lock)
{
text = clickedLine < _lines.Count
? $"[{_lines[clickedLine].timestamp}] {_lines[clickedLine].text}"
: "";
}
EditorGUIUtility.systemCopyBuffer = text;
});
menu.ShowAsContext();
evt.Use();
menu.AddItem(new GUIContent("Copy selected lines"), false, () => CopyRange(selMin, selMax));
menu.AddSeparator("");
}
menu.AddItem(new GUIContent("Copy this line"), false, () =>
{
string text;
lock (_lock)
{
text = clickedLine < _lines.Count
? $"[{_lines[clickedLine].timestamp}] {_lines[clickedLine].text}"
: "";
}
EditorGUIUtility.systemCopyBuffer = text;
});
menu.ShowAsContext();
evt.Use();
}
}
}

View File

@@ -22,11 +22,12 @@ namespace SplashEdit.EditorCode
{
// ───── Constants ─────
private const string WINDOW_TITLE = "SplashEdit Control Panel";
private const string MENU_PATH = "PlayStation 1/SplashEdit Control Panel %#p";
private const string MENU_PATH = "PlayStation 1/SplashEdit Control Panel %#l";
// ───── UI State ─────
private Vector2 _scrollPos;
private bool _showQuickStart = true;
private int _selectedTab = 0;
private static readonly string[] _tabNames = { "Dependencies", "Scenes", "Build" };
private bool _showNativeProject = true;
private bool _showToolchainSection = true;
private bool _showScenesSection = true;
@@ -84,12 +85,10 @@ namespace SplashEdit.EditorCode
RefreshToolchainStatus();
LoadSceneList();
_manualNativePath = SplashSettings.NativeProjectPath;
EditorApplication.playModeStateChanged += OnPlayModeChanged;
}
private void OnDisable()
{
EditorApplication.playModeStateChanged -= OnPlayModeChanged;
}
private void OnFocus()
@@ -97,61 +96,40 @@ namespace SplashEdit.EditorCode
RefreshToolchainStatus();
}
// ═══════════════════════════════════════════════════════════════
// Play Mode Intercept
// ═══════════════════════════════════════════════════════════════
private void OnPlayModeChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.ExitingEditMode && SplashSettings.InterceptPlayMode)
{
EditorApplication.isPlaying = false;
Log("Play Mode intercepted — starting Build & Run instead.", LogType.Log);
BuildAndRun();
}
}
// ═══════════════════════════════════════════════════════════════
// Main GUI
// ═══════════════════════════════════════════════════════════════
private void OnGUI()
{
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
if (_isRunning && _pcdrvHost != null && !_pcdrvHost.IsRunning)
{
StopAll();
Log("PCdrv host connection lost.", LogType.Warning);
}
DrawHeader();
EditorGUILayout.Space(4);
// Show Quick Start prominently if toolchain is not ready
if (!_hasMIPS || !_hasNativeProject)
_selectedTab = PSXEditorStyles.DrawButtonGroup(_tabNames, _selectedTab, 28);
EditorGUILayout.Space(4);
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
switch (_selectedTab)
{
DrawQuickStartSection();
EditorGUILayout.Space(2);
case 0: // Dependencies
DrawToolchainSection();
EditorGUILayout.Space(2);
DrawNativeProjectSection();
break;
case 1: // Scenes
DrawScenesSection();
break;
case 2: // Build
DrawBuildSection();
break;
}
else
{
// Collapsed quick start for experienced users
_showQuickStart = DrawSectionFoldout("Quick Start Guide", _showQuickStart);
if (_showQuickStart)
{
DrawQuickStartContent();
}
EditorGUILayout.Space(2);
}
DrawNativeProjectSection();
EditorGUILayout.Space(2);
DrawToolchainSection();
EditorGUILayout.Space(2);
DrawScenesSection();
EditorGUILayout.Space(2);
DrawVRAMSection();
EditorGUILayout.Space(2);
DrawBuildSection();
EditorGUILayout.EndScrollView();
}
@@ -167,19 +145,9 @@ namespace SplashEdit.EditorCode
GUILayout.Label("SplashEdit", PSXEditorStyles.WindowHeader);
GUILayout.FlexibleSpace();
// Play mode intercept toggle
bool intercept = SplashSettings.InterceptPlayMode;
var toggleContent = new GUIContent(
intercept ? "▶ Intercept ON" : "▶ Intercept OFF",
"When enabled, pressing Play in Unity triggers Build & Run instead.");
bool newIntercept = GUILayout.Toggle(intercept, toggleContent, EditorStyles.toolbarButton, GUILayout.Width(120));
if (newIntercept != intercept)
SplashSettings.InterceptPlayMode = newIntercept;
EditorGUILayout.EndHorizontal();
// Status bar
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
{
string statusText;
Color statusColor;
@@ -210,90 +178,13 @@ namespace SplashEdit.EditorCode
statusColor = PSXEditorStyles.Success;
}
var prevColor = GUI.contentColor;
GUI.contentColor = statusColor;
GUILayout.Label(statusText, EditorStyles.miniLabel);
GUI.contentColor = prevColor;
EditorGUILayout.BeginHorizontal(PSXEditorStyles.InfoBox);
PSXEditorStyles.DrawStatusBadge(statusColor == PSXEditorStyles.Success ? "OK" :
statusColor == PSXEditorStyles.Warning ? "WARN" :
statusColor == PSXEditorStyles.Info ? "INFO" : "RUN", statusColor, 50);
GUILayout.Label(statusText, PSXEditorStyles.RichLabel);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndHorizontal();
}
// ═══════════════════════════════════════════════════════════════
// Quick Start Guide
// ═══════════════════════════════════════════════════════════════
private void DrawQuickStartSection()
{
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
var prevColor = GUI.contentColor;
GUI.contentColor = PSXEditorStyles.AccentGold;
GUILayout.Label("Getting Started with SplashEdit", PSXEditorStyles.CardHeaderStyle);
GUI.contentColor = prevColor;
EditorGUILayout.Space(4);
DrawQuickStartContent();
EditorGUILayout.EndVertical();
}
private void DrawQuickStartContent()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawQuickStartStep(1, "Install Toolchain",
"Install the MIPS cross-compiler and GNU Make below.",
_hasMIPS && _hasMake);
DrawQuickStartStep(2, "Get Native Project",
"Clone the psxsplash runtime or set a path to your local copy.",
_hasNativeProject);
DrawQuickStartStep(3, "Add Scenes",
"Add Unity scenes containing a PSXSceneExporter to the scene list.",
_sceneList.Count > 0);
DrawQuickStartStep(4, "Configure VRAM",
"Set the framebuffer resolution and texture packing settings.",
true); // Always "done" since defaults are fine
DrawQuickStartStep(5, "Build & Run",
"Click BUILD & RUN to export, compile, and launch on the emulator or real hardware.",
false);
EditorGUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Open Documentation", EditorStyles.miniButton, GUILayout.Width(140)))
{
Application.OpenURL("https://github.com/psxsplash/splashedit");
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void DrawQuickStartStep(int step, string title, string description, bool done)
{
EditorGUILayout.BeginHorizontal();
// Checkbox/step indicator
string prefix = done ? "✓" : $"{step}.";
var style = done ? EditorStyles.miniLabel : EditorStyles.boldLabel;
var prevColor = GUI.contentColor;
GUI.contentColor = done ? PSXEditorStyles.Success : PSXEditorStyles.TextPrimary;
GUILayout.Label(prefix, style, GUILayout.Width(20));
GUI.contentColor = prevColor;
EditorGUILayout.BeginVertical();
GUILayout.Label(title, EditorStyles.boldLabel);
GUILayout.Label(description, EditorStyles.wordWrappedMiniLabel);
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(2);
}
// ═══════════════════════════════════════════════════════════════
@@ -327,7 +218,8 @@ namespace SplashEdit.EditorCode
EditorGUILayout.Space(6);
// ── Option 1: Auto-clone from GitHub ──
EditorGUILayout.LabelField("Clone from GitHub", EditorStyles.boldLabel);
PSXEditorStyles.DrawSeparator(4, 4);
GUILayout.Label("Clone from GitHub", PSXEditorStyles.SectionHeader);
EditorGUILayout.BeginHorizontal();
GUILayout.Label(PSXSplashInstaller.RepoUrl, EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
@@ -341,7 +233,7 @@ namespace SplashEdit.EditorCode
if (_isInstallingNative)
{
EditorGUILayout.HelpBox(_nativeInstallStatus, MessageType.Info);
GUILayout.Label(_nativeInstallStatus, PSXEditorStyles.InfoBox);
}
// If already cloned, show version management
@@ -362,7 +254,8 @@ namespace SplashEdit.EditorCode
EditorGUILayout.Space(6);
// ── Option 2: Manual path ──
EditorGUILayout.LabelField("Or set path manually", EditorStyles.boldLabel);
PSXEditorStyles.DrawSeparator(4, 4);
GUILayout.Label("Or set path manually", PSXEditorStyles.SectionHeader);
EditorGUILayout.BeginHorizontal();
string newPath = EditorGUILayout.TextField(_manualNativePath);
@@ -389,7 +282,7 @@ namespace SplashEdit.EditorCode
EditorGUILayout.BeginHorizontal();
if (!string.IsNullOrEmpty(_manualNativePath) && !manualPathValid)
{
EditorGUILayout.HelpBox("Invalid path. The directory must contain a Makefile.", MessageType.Warning);
GUILayout.Label("Invalid path. The directory must contain a Makefile.", PSXEditorStyles.InfoBox);
}
else if (manualPathValid && _manualNativePath != SplashSettings.NativeProjectPath)
{
@@ -431,15 +324,17 @@ namespace SplashEdit.EditorCode
GUILayout.FlexibleSpace();
if (!_hasMIPS)
{
if (GUILayout.Button("Install", GUILayout.Width(60)))
if (GUILayout.Button("Install", PSXEditorStyles.SecondaryButton, GUILayout.Width(70)))
InstallMIPS();
}
else
{
GUILayout.Label("Ready", EditorStyles.miniLabel);
PSXEditorStyles.DrawStatusBadge("Ready", PSXEditorStyles.Success);
}
EditorGUILayout.EndHorizontal();
PSXEditorStyles.DrawSeparator(2, 2);
// GNU Make
EditorGUILayout.BeginHorizontal();
DrawStatusIcon(_hasMake);
@@ -447,15 +342,17 @@ namespace SplashEdit.EditorCode
GUILayout.FlexibleSpace();
if (!_hasMake)
{
if (GUILayout.Button("Install", GUILayout.Width(60)))
if (GUILayout.Button("Install", PSXEditorStyles.SecondaryButton, GUILayout.Width(70)))
InstallMake();
}
else
{
GUILayout.Label("Ready", EditorStyles.miniLabel);
PSXEditorStyles.DrawStatusBadge("Ready", PSXEditorStyles.Success);
}
EditorGUILayout.EndHorizontal();
PSXEditorStyles.DrawSeparator(2, 2);
// PCSX-Redux
EditorGUILayout.BeginHorizontal();
DrawStatusIcon(_hasRedux);
@@ -463,15 +360,17 @@ namespace SplashEdit.EditorCode
GUILayout.FlexibleSpace();
if (!_hasRedux)
{
if (GUILayout.Button("Download", GUILayout.Width(80)))
if (GUILayout.Button("Download", PSXEditorStyles.SecondaryButton, GUILayout.Width(80)))
DownloadRedux();
}
else
{
GUILayout.Label(_reduxVersion, EditorStyles.miniLabel);
PSXEditorStyles.DrawStatusBadge(_reduxVersion, PSXEditorStyles.Success);
}
EditorGUILayout.EndHorizontal();
PSXEditorStyles.DrawSeparator(2, 2);
// psxavenc (audio encoder)
EditorGUILayout.BeginHorizontal();
DrawStatusIcon(_hasPsxavenc);
@@ -479,15 +378,17 @@ namespace SplashEdit.EditorCode
GUILayout.FlexibleSpace();
if (!_hasPsxavenc)
{
if (GUILayout.Button("Download", GUILayout.Width(80)))
if (GUILayout.Button("Download", PSXEditorStyles.SecondaryButton, GUILayout.Width(80)))
DownloadPsxavenc();
}
else
{
GUILayout.Label("Installed", EditorStyles.miniLabel);
PSXEditorStyles.DrawStatusBadge("Installed", PSXEditorStyles.Success);
}
EditorGUILayout.EndHorizontal();
PSXEditorStyles.DrawSeparator(2, 2);
// mkpsxiso (ISO builder)
EditorGUILayout.BeginHorizontal();
DrawStatusIcon(_hasMkpsxiso);
@@ -495,12 +396,12 @@ namespace SplashEdit.EditorCode
GUILayout.FlexibleSpace();
if (!_hasMkpsxiso)
{
if (GUILayout.Button("Download", GUILayout.Width(80)))
if (GUILayout.Button("Download", PSXEditorStyles.SecondaryButton, GUILayout.Width(80)))
DownloadMkpsxiso();
}
else
{
GUILayout.Label("Installed", EditorStyles.miniLabel);
PSXEditorStyles.DrawStatusBadge("Installed", PSXEditorStyles.Success);
}
EditorGUILayout.EndHorizontal();
@@ -528,11 +429,11 @@ namespace SplashEdit.EditorCode
if (_sceneList.Count == 0)
{
EditorGUILayout.HelpBox(
GUILayout.Label(
"No scenes added yet.\n" +
"Each scene needs a GameObject with a PSXSceneExporter component.\n" +
"Drag scene assets here, or use the buttons below to add them.",
MessageType.Info);
PSXEditorStyles.InfoBox);
}
// Draw scene list
@@ -640,49 +541,19 @@ namespace SplashEdit.EditorCode
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
// Resolution settings
EditorGUILayout.LabelField("Framebuffer Settings", EditorStyles.boldLabel);
// Framebuffer: hardcoded 320x240, vertical, dual-buffered
GUILayout.Label("Framebuffer", PSXEditorStyles.SectionHeader);
GUILayout.Label("Resolution: 320x240 (dual-buffered, vertical layout)", PSXEditorStyles.InfoBox);
PSXEditorStyles.DrawSeparator(4, 4);
GUILayout.Label("Advanced Tools", PSXEditorStyles.SectionHeader);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Resolution:", GUILayout.Width(75));
SplashSettings.ResolutionWidth = EditorGUILayout.IntField(SplashSettings.ResolutionWidth, GUILayout.Width(50));
GUILayout.Label("×", GUILayout.Width(14));
SplashSettings.ResolutionHeight = EditorGUILayout.IntField(SplashSettings.ResolutionHeight, GUILayout.Width(50));
// Common resolutions dropdown
if (GUILayout.Button("Presets", EditorStyles.miniButton, GUILayout.Width(55)))
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("256×240"), false, () => { SplashSettings.ResolutionWidth = 256; SplashSettings.ResolutionHeight = 240; Repaint(); });
menu.AddItem(new GUIContent("320×240"), false, () => { SplashSettings.ResolutionWidth = 320; SplashSettings.ResolutionHeight = 240; Repaint(); });
menu.AddItem(new GUIContent("368×240"), false, () => { SplashSettings.ResolutionWidth = 368; SplashSettings.ResolutionHeight = 240; Repaint(); });
menu.AddItem(new GUIContent("512×240"), false, () => { SplashSettings.ResolutionWidth = 512; SplashSettings.ResolutionHeight = 240; Repaint(); });
menu.AddItem(new GUIContent("640×240"), false, () => { SplashSettings.ResolutionWidth = 640; SplashSettings.ResolutionHeight = 240; Repaint(); });
menu.AddItem(new GUIContent("256×480"), false, () => { SplashSettings.ResolutionWidth = 256; SplashSettings.ResolutionHeight = 480; Repaint(); });
menu.AddItem(new GUIContent("320×480"), false, () => { SplashSettings.ResolutionWidth = 320; SplashSettings.ResolutionHeight = 480; Repaint(); });
menu.AddItem(new GUIContent("512×480"), false, () => { SplashSettings.ResolutionWidth = 512; SplashSettings.ResolutionHeight = 480; Repaint(); });
menu.ShowAsContext();
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
SplashSettings.DualBuffering = EditorGUILayout.Toggle("Dual Buffering", SplashSettings.DualBuffering);
SplashSettings.VerticalLayout = EditorGUILayout.Toggle("Vertical Layout", SplashSettings.VerticalLayout);
EditorGUILayout.Space(4);
EditorGUILayout.LabelField("Export Settings", EditorStyles.boldLabel);
SplashSettings.DefaultGTEScaling = EditorGUILayout.FloatField("Default GTE Scaling", SplashSettings.DefaultGTEScaling);
EditorGUILayout.Space(6);
EditorGUILayout.LabelField("Advanced Tools", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Open VRAM Editor", GUILayout.Height(24)))
if (GUILayout.Button("Open VRAM Editor", PSXEditorStyles.PrimaryButton, GUILayout.Height(26)))
{
VRAMEditorWindow.ShowWindow();
}
if (GUILayout.Button("Quantized Preview", GUILayout.Height(24)))
if (GUILayout.Button("Quantized Preview", PSXEditorStyles.PrimaryButton, GUILayout.Height(26)))
{
QuantizedPreviewWindow.ShowWindow();
}
@@ -703,6 +574,7 @@ namespace SplashEdit.EditorCode
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
// Target & Mode
GUILayout.Label("Configuration", PSXEditorStyles.SectionHeader);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Target:", GUILayout.Width(50));
SplashSettings.Target = (BuildTarget)EditorGUILayout.EnumPopup(SplashSettings.Target);
@@ -710,6 +582,9 @@ namespace SplashEdit.EditorCode
SplashSettings.Mode = (BuildMode)EditorGUILayout.EnumPopup(SplashSettings.Mode);
EditorGUILayout.EndHorizontal();
// Clean Build toggle
SplashSettings.CleanBuild = EditorGUILayout.Toggle("Clean Build", SplashSettings.CleanBuild);
// Serial port (only for Real Hardware)
if (SplashSettings.Target == BuildTarget.RealHardware)
{
@@ -749,53 +624,55 @@ namespace SplashEdit.EditorCode
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.Space(8);
PSXEditorStyles.DrawSeparator(6, 6);
// Big Build & Run button
EditorGUI.BeginDisabledGroup(_isBuilding);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
var buildColor = GUI.backgroundColor;
GUI.backgroundColor = _isBuilding ? Color.gray : new Color(0.3f, 0.8f, 0.4f);
var largeBuildButton = new GUIStyle(PSXEditorStyles.SuccessButton)
{
fontSize = 14,
fontStyle = FontStyle.Bold,
padding = new RectOffset(20, 20, 10, 10)
};
string buttonLabel = _isBuilding ? "Building..." : "BUILD & RUN";
if (GUILayout.Button(buttonLabel, GUILayout.Width(200), GUILayout.Height(36)))
string buttonLabel = _isBuilding ? "Building..." :
(SplashSettings.Target == BuildTarget.ISO ? "BUILD" : "BUILD & RUN");
if (GUILayout.Button(buttonLabel, largeBuildButton, GUILayout.Width(200), GUILayout.Height(38)))
{
BuildAndRun();
}
GUI.backgroundColor = buildColor;
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
// Stop button (if running emulator or hardware PCdrv host)
// Stop button (if running - emulator or hardware PCdrv host)
if (_isRunning)
{
EditorGUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUI.backgroundColor = new Color(0.9f, 0.3f, 0.3f);
string stopLabel = _emulatorProcess != null ? "■ STOP EMULATOR" : "■ STOP PCdrv HOST";
if (GUILayout.Button(stopLabel, GUILayout.Width(200), GUILayout.Height(24)))
string stopLabel = _emulatorProcess != null ? "STOP EMULATOR" : "STOP PCdrv HOST";
if (GUILayout.Button(stopLabel, PSXEditorStyles.DangerButton, GUILayout.Width(200), GUILayout.Height(26)))
{
StopAll();
}
GUI.backgroundColor = buildColor;
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
// Export-only / Compile-only
EditorGUILayout.Space(4);
PSXEditorStyles.DrawSeparator(4, 4);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Export Only", EditorStyles.miniButton, GUILayout.Width(100)))
if (GUILayout.Button("Export Only", PSXEditorStyles.SecondaryButton, GUILayout.Width(100)))
{
ExportAllScenes();
}
if (GUILayout.Button("Compile Only", EditorStyles.miniButton, GUILayout.Width(100)))
if (GUILayout.Button("Compile Only", PSXEditorStyles.SecondaryButton, GUILayout.Width(100)))
{
CompileOnly();
}
@@ -815,6 +692,20 @@ namespace SplashEdit.EditorCode
public async void BuildAndRun()
{
if (_isBuilding) return;
if (UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().isDirty)
{
int choice = EditorUtility.DisplayDialogComplex(
"Unsaved Changes",
"The current scene has unsaved changes. Save before building?",
"Save and Build", // 0
"Cancel", // 1
"Build Without Saving" // 2
);
if (choice == 1) return; // Cancel
if (choice == 0) EditorSceneManager.SaveOpenScenes();
}
_isBuilding = true;
var console = EditorWindow.GetWindow<PSXConsoleWindow>();
@@ -1126,15 +1017,17 @@ namespace SplashEdit.EditorCode
if (SplashSettings.Target == BuildTarget.ISO)
buildArg += " LOADER=cdrom";
string makeCmd = $"make clean && make all -j{SystemInfo.processorCount} {buildArg}".Trim();
int jobCount = Math.Max(1, SystemInfo.processorCount - 1);
string cleanPrefix = SplashSettings.CleanBuild ? "make clean && " : "";
string makeCmd = $"{cleanPrefix}make all -j{jobCount} {buildArg}".Trim();
Log($"Running: {makeCmd}", LogType.Log);
var psi = new ProcessStartInfo
{
FileName = Application.platform == RuntimePlatform.WindowsEditor ? "cmd.exe" : "/bin/bash",
Arguments = Application.platform == RuntimePlatform.WindowsEditor
? $"/c {makeCmd}"
: $"-c \"{makeCmd}\"",
? $"/c \"cd /d \"{nativeDir}\" && {makeCmd}\""
: $"-c \"cd \\\"{nativeDir}\\\" && {makeCmd}\"",
WorkingDirectory = nativeDir,
RedirectStandardOutput = true,
RedirectStandardError = true,
@@ -1269,7 +1162,7 @@ namespace SplashEdit.EditorCode
StopAllQuiet();
string pcdrvBase = SplashBuildPaths.BuildOutputDir;
string args = $"-exe \"{exePath}\" -run -fastboot -pcdrv -pcdrvbase \"{pcdrvBase}\" -stdout";
string args = $"-exe \"{exePath}\" -run -fastboot -pcdrv -pcdrvbase \"{pcdrvBase}\" -pad1type dualshock -stdout";
Log($"Launching: {Path.GetFileName(reduxPath)} {args}", LogType.Log);
@@ -1289,6 +1182,15 @@ namespace SplashEdit.EditorCode
try
{
_emulatorProcess = Process.Start(psi);
_emulatorProcess.EnableRaisingEvents = true;
_emulatorProcess.Exited += (s, e) => {
EditorApplication.delayCall += () => {
_isRunning = false;
_emulatorProcess = null;
PSXConsoleWindow.Detach();
Repaint();
};
};
_isRunning = true;
Log("PCSX-Redux launched.", LogType.Log);

View File

@@ -92,29 +92,36 @@ namespace SplashEdit.EditorCode
set => EditorPrefs.SetInt(Prefix + "SerialBaudRate", value);
}
// --- VRAM Layout ---
// --- VRAM Layout (hardcoded 320x240, dual-buffered, vertical) ---
public static int ResolutionWidth
{
get => EditorPrefs.GetInt(Prefix + "ResWidth", 320);
set => EditorPrefs.SetInt(Prefix + "ResWidth", value);
get => 320;
set { } // no-op, hardcoded
}
public static int ResolutionHeight
{
get => EditorPrefs.GetInt(Prefix + "ResHeight", 240);
set => EditorPrefs.SetInt(Prefix + "ResHeight", value);
get => 240;
set { } // no-op, hardcoded
}
public static bool DualBuffering
{
get => EditorPrefs.GetBool(Prefix + "DualBuffering", true);
set => EditorPrefs.SetBool(Prefix + "DualBuffering", value);
get => true;
set { } // no-op, hardcoded
}
public static bool VerticalLayout
{
get => EditorPrefs.GetBool(Prefix + "VerticalLayout", true);
set => EditorPrefs.SetBool(Prefix + "VerticalLayout", value);
get => true;
set { } // no-op, hardcoded
}
// --- Clean Build ---
public static bool CleanBuild
{
get => EditorPrefs.GetBool(Prefix + "CleanBuild", true);
set => EditorPrefs.SetBool(Prefix + "CleanBuild", value);
}
// --- Export settings ---
@@ -124,13 +131,6 @@ namespace SplashEdit.EditorCode
set => EditorPrefs.SetFloat(Prefix + "GTEScaling", value);
}
// --- Play Mode Intercept ---
public static bool InterceptPlayMode
{
get => EditorPrefs.GetBool(Prefix + "InterceptPlayMode", false);
set => EditorPrefs.SetBool(Prefix + "InterceptPlayMode", value);
}
// --- ISO Build ---
/// <summary>
/// Optional path to a Sony license file (.dat) for the ISO image.
@@ -162,7 +162,7 @@ namespace SplashEdit.EditorCode
"Target", "Mode", "NativeProjectPath", "MIPSToolchainPath",
"PCSXReduxPath", "PCSXReduxPCdrvBase", "SerialPort", "SerialBaudRate",
"ResWidth", "ResHeight", "DualBuffering", "VerticalLayout",
"GTEScaling", "AutoValidate", "InterceptPlayMode",
"GTEScaling", "AutoValidate",
"LicenseFilePath", "ISOVolumeLabel"
};