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

@@ -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);