Lua bytecode builder, cdrom fixes

This commit is contained in:
Jan Racek
2026-03-28 19:41:53 +01:00
parent 275b4e891d
commit 61fbca17a7
5 changed files with 555 additions and 15 deletions

View File

@@ -43,9 +43,9 @@ namespace SplashEdit.EditorCode
public const long SPU_RESERVED = 0x1010; public const long SPU_RESERVED = 0x1010;
public const long USABLE_SPU = TOTAL_SPU - SPU_RESERVED; public const long USABLE_SPU = TOTAL_SPU - SPU_RESERVED;
// Fixed runtime overhead from C++ (renderer.hh constants) // Fixed runtime overhead from C++ (renderer.hh constants, now configurable)
public const long BUMP_ALLOC_TOTAL = 2L * 8096 * 24; // ~380KB public static long BUMP_ALLOC_TOTAL => 2L * SplashSettings.BumpSize;
public const long OT_TOTAL = 2L * 16384 * 4; // ~128KB public static long OT_TOTAL => 2L * SplashSettings.OtSize * 4;
public const long VIS_REFS = 4096 * 4; // 16KB public const long VIS_REFS = 4096 * 4; // 16KB
public const long STACK_ESTIMATE = 32 * 1024; // 32KB public const long STACK_ESTIMATE = 32 * 1024; // 32KB
public const long LUA_OVERHEAD = 16 * 1024; // 16KB approximate public const long LUA_OVERHEAD = 16 * 1024; // 16KB approximate
@@ -53,6 +53,11 @@ namespace SplashEdit.EditorCode
public long FixedOverhead => BUMP_ALLOC_TOTAL + OT_TOTAL + VIS_REFS + STACK_ESTIMATE + LUA_OVERHEAD; public long FixedOverhead => BUMP_ALLOC_TOTAL + OT_TOTAL + VIS_REFS + STACK_ESTIMATE + LUA_OVERHEAD;
// Heap estimate and warnings
public long EstimatedHeapFree => USABLE_RAM - TotalRamUsage;
public bool IsHeapWarning => EstimatedHeapFree < 128 * 1024; // < 128KB free
public bool IsHeapCritical => EstimatedHeapFree < 64 * 1024; // < 64KB free
/// <summary>RAM used by scene data (live portion of splashpack).</summary> /// <summary>RAM used by scene data (live portion of splashpack).</summary>
public long SceneRamUsage => splashpackLiveSize > 0 ? splashpackLiveSize : splashpackFileSize; public long SceneRamUsage => splashpackLiveSize > 0 ? splashpackLiveSize : splashpackFileSize;

View File

@@ -166,6 +166,45 @@ namespace SplashEdit.EditorCode
EnsureGitIgnore(); EnsureGitIgnore();
} }
// ───── Lua bytecode compilation paths ─────
/// <summary>
/// Directory for Lua source files extracted during export.
/// </summary>
public static string LuaSrcDir =>
Path.Combine(BuildOutputDir, "lua_src");
/// <summary>
/// Directory for compiled Lua bytecode files.
/// </summary>
public static string LuaCompiledDir =>
Path.Combine(BuildOutputDir, "lua_compiled");
/// <summary>
/// Manifest file listing input/output pairs for the PS1 Lua compiler.
/// </summary>
public static string LuaManifestPath =>
Path.Combine(LuaSrcDir, "manifest.txt");
/// <summary>
/// Sentinel file written by luac_psx when compilation is complete.
/// Contains "OK" on success or "ERROR" on failure.
/// </summary>
public static string LuaDoneSentinel =>
Path.Combine(LuaSrcDir, "__done__");
/// <summary>
/// Path to the luac_psx PS1 compiler executable (built from tools/luac_psx/).
/// </summary>
public static string LuacPsxExePath =>
Path.Combine(NativeSourceDir, "tools", "luac_psx", "luac_psx.ps-exe");
/// <summary>
/// Path to the luac_psx tools directory (for building the compiler).
/// </summary>
public static string LuacPsxDir =>
Path.Combine(NativeSourceDir, "tools", "luac_psx");
/// <summary> /// <summary>
/// Checks if PCSX-Redux is installed in the tools directory. /// Checks if PCSX-Redux is installed in the tools directory.
/// </summary> /// </summary>

View File

@@ -37,6 +37,7 @@ namespace SplashEdit.EditorCode
// ───── Build State ───── // ───── Build State ─────
private static bool _isBuilding; private static bool _isBuilding;
private static bool _isRunning; private static bool _isRunning;
private static bool _luaBytecodeCompiled;
private static Process _emulatorProcess; private static Process _emulatorProcess;
// ───── Scene List ───── // ───── Scene List ─────
@@ -669,6 +670,13 @@ namespace SplashEdit.EditorCode
new GUIContent("FPS Overlay", "Show an FPS counter at top-left during gameplay"), new GUIContent("FPS Overlay", "Show an FPS counter at top-left during gameplay"),
SplashSettings.FpsOverlay); SplashSettings.FpsOverlay);
SplashSettings.OtSize = EditorGUILayout.IntField(
new GUIContent("OT Size", "Ordering table entries. Lower = less RAM, shallower Z-sorting."),
SplashSettings.OtSize);
SplashSettings.BumpSize = EditorGUILayout.IntField(
new GUIContent("Bump Alloc Size", "Per-frame primitive buffer (bytes). Lower = less RAM, fewer triangles per frame."),
SplashSettings.BumpSize);
// Serial port (only for Real Hardware) // Serial port (only for Real Hardware)
if (SplashSettings.Target == BuildTarget.RealHardware) if (SplashSettings.Target == BuildTarget.RealHardware)
{ {
@@ -789,14 +797,18 @@ namespace SplashEdit.EditorCode
GUILayout.Label($"Scene: {report.sceneName}", PSXEditorStyles.SectionHeader); GUILayout.Label($"Scene: {report.sceneName}", PSXEditorStyles.SectionHeader);
EditorGUILayout.Space(4); EditorGUILayout.Space(4);
// Main RAM bar // Main RAM bar — segmented: OT | Bump | Scene | Heap
DrawMemoryBar("Main RAM", DrawSegmentedMemoryBar("Main RAM",
report.TotalRamUsage, SceneMemoryReport.USABLE_RAM, SceneMemoryReport.USABLE_RAM,
report.RamPercent, report.RamPercent,
new Color(0.3f, 0.6f, 1f), new (string label, long size, Color color)[] {
$"Scene: {FormatBytes(report.SceneRamUsage)} | " + ("OT", SceneMemoryReport.OT_TOTAL, new Color(0.9f, 0.4f, 0.2f)),
$"Fixed: {FormatBytes(report.FixedOverhead)} | " + ("Bump", SceneMemoryReport.BUMP_ALLOC_TOTAL, new Color(0.8f, 0.6f, 0.1f)),
$"Free: {FormatBytes(report.RamFree)}"); ("Scene", report.SceneRamUsage, new Color(0.3f, 0.6f, 1.0f)),
("Other", SceneMemoryReport.VIS_REFS + SceneMemoryReport.STACK_ESTIMATE + SceneMemoryReport.LUA_OVERHEAD,
new Color(0.5f, 0.5f, 0.5f)),
},
report.EstimatedHeapFree);
EditorGUILayout.Space(4); EditorGUILayout.Space(4);
@@ -842,6 +854,15 @@ namespace SplashEdit.EditorCode
$"<b>{report.clutCount}</b> CLUTs", $"<b>{report.clutCount}</b> CLUTs",
PSXEditorStyles.RichLabel); PSXEditorStyles.RichLabel);
if (report.IsHeapCritical)
EditorGUILayout.HelpBox(
$"Estimated heap: {report.EstimatedHeapFree / 1024}KB free. Lua scripts may OOM!",
MessageType.Error);
else if (report.IsHeapWarning)
EditorGUILayout.HelpBox(
$"Estimated heap: {report.EstimatedHeapFree / 1024}KB free. Consider reducing OT/Bump sizes.",
MessageType.Warning);
EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical();
EditorGUILayout.Space(4); EditorGUILayout.Space(4);
} }
@@ -889,6 +910,62 @@ namespace SplashEdit.EditorCode
GUILayout.Label(details, EditorStyles.miniLabel); GUILayout.Label(details, EditorStyles.miniLabel);
} }
private void DrawSegmentedMemoryBar(
string label, long total, float percent,
(string label, long size, Color color)[] segments,
long heapFree)
{
long used = 0;
foreach (var seg in segments) used += seg.size;
// Label row
EditorGUILayout.BeginHorizontal();
GUILayout.Label(label, EditorStyles.boldLabel, GUILayout.Width(70));
GUILayout.Label($"{FormatBytes(used)} / {FormatBytes(total)} ({percent:F1}%) | Heap free: {FormatBytes(heapFree)}",
EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
// Bar
Rect barRect = GUILayoutUtility.GetRect(0, 16, GUILayout.ExpandWidth(true));
EditorGUI.DrawRect(barRect, new Color(0.15f, 0.15f, 0.17f));
// Draw each segment
float x = barRect.x;
foreach (var seg in segments)
{
float w = barRect.width * Mathf.Clamp01((float)seg.size / total);
if (w > 0)
EditorGUI.DrawRect(new Rect(x, barRect.y, w, barRect.height), seg.color);
x += w;
}
// Border
DrawRectOutline(barRect, new Color(0.3f, 0.3f, 0.35f));
// Percent overlay
var style = new GUIStyle(EditorStyles.miniLabel)
{
alignment = TextAnchor.MiddleCenter,
normal = { textColor = Color.white }
};
GUI.Label(barRect, $"{percent:F1}%", style);
// Legend row
EditorGUILayout.BeginHorizontal();
foreach (var seg in segments)
{
// Color swatch + label
Rect swatchRect = GUILayoutUtility.GetRect(10, 10, GUILayout.Width(10));
swatchRect.y += 2;
EditorGUI.DrawRect(swatchRect, seg.color);
GUILayout.Label($"{seg.label}: {FormatBytes(seg.size)}", EditorStyles.miniLabel);
GUILayout.Space(6);
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
private static void DrawRectOutline(Rect rect, Color color) private static void DrawRectOutline(Rect rect, Color color)
{ {
EditorGUI.DrawRect(new Rect(rect.x, rect.y, rect.width, 1), color); EditorGUI.DrawRect(new Rect(rect.x, rect.y, rect.width, 1), color);
@@ -930,6 +1007,7 @@ namespace SplashEdit.EditorCode
} }
_isBuilding = true; _isBuilding = true;
_luaBytecodeCompiled = false;
var console = EditorWindow.GetWindow<PSXConsoleWindow>(); var console = EditorWindow.GetWindow<PSXConsoleWindow>();
console.titleContent = new GUIContent("PSX Console", EditorGUIUtility.IconContent("d_UnityEditor.ConsoleWindow").image); console.titleContent = new GUIContent("PSX Console", EditorGUIUtility.IconContent("d_UnityEditor.ConsoleWindow").image);
@@ -946,7 +1024,32 @@ namespace SplashEdit.EditorCode
} }
Log("Toolchain OK.", LogType.Log); Log("Toolchain OK.", LogType.Log);
// Collect Lua files from all scenes and compile to bytecode
Log("Collecting Lua scripts...", LogType.Log);
EditorUtility.DisplayProgressBar("SplashEdit", "Collecting Lua scripts...", 0.2f);
var luaFiles = CollectLuaSources();
if (luaFiles != null && luaFiles.Count > 0)
{
Log($"Found {luaFiles.Count} Lua script(s). Compiling...", LogType.Log);
EditorUtility.DisplayProgressBar("SplashEdit", "Compiling Lua scripts...", 0.3f);
var bytecodeMap = await CompileLuaAsync(luaFiles);
if (bytecodeMap == null)
{
Log("Lua compilation failed.", LogType.Error);
return;
}
PSXSceneWriter.CompiledLuaBytecode = bytecodeMap;
_luaBytecodeCompiled = true;
Log($"Compiled {bytecodeMap.Count} Lua script(s) to bytecode.", LogType.Log);
}
else
{
Log("No Lua scripts found.", LogType.Log);
PSXSceneWriter.CompiledLuaBytecode = null;
}
Log("Exporting scenes...", LogType.Log); Log("Exporting scenes...", LogType.Log);
EditorUtility.DisplayProgressBar("SplashEdit", "Exporting scenes...", 0.4f);
if (!ExportAllScenes()) if (!ExportAllScenes())
{ {
Log("Export failed.", LogType.Error); Log("Export failed.", LogType.Error);
@@ -954,6 +1057,9 @@ namespace SplashEdit.EditorCode
} }
Log($"Exported {_sceneList.Count} scene(s).", LogType.Log); Log($"Exported {_sceneList.Count} scene(s).", LogType.Log);
// Clear bytecode cache after export
PSXSceneWriter.CompiledLuaBytecode = null;
Log("Compiling native code...", LogType.Log); Log("Compiling native code...", LogType.Log);
EditorUtility.DisplayProgressBar("SplashEdit", "Compiling native code...", 0.6f); EditorUtility.DisplayProgressBar("SplashEdit", "Compiling native code...", 0.6f);
if (!await CompileNativeAsync()) if (!await CompileNativeAsync())
@@ -1222,6 +1328,345 @@ namespace SplashEdit.EditorCode
Log("Wrote scene manifest.", LogType.Log); Log("Wrote scene manifest.", LogType.Log);
} }
// ───── Lua bytecode compilation ─────
/// <summary>
/// Scan all scenes in the scene list and collect unique Lua source files.
/// Writes each source to PSXBuild/lua_src/ for compilation.
/// </summary>
private Dictionary<LuaFile, string> CollectLuaSources()
{
SplashBuildPaths.EnsureDirectories();
string luaSrcDir = SplashBuildPaths.LuaSrcDir;
if (Directory.Exists(luaSrcDir))
Directory.Delete(luaSrcDir, true);
Directory.CreateDirectory(luaSrcDir);
string currentScenePath = SceneManager.GetActiveScene().path;
var luaFileMap = new Dictionary<LuaFile, string>(); // LuaFile -> filename (no extension)
for (int i = 0; i < _sceneList.Count; i++)
{
var scene = _sceneList[i];
if (scene.asset == null) continue;
try
{
EditorSceneManager.OpenScene(scene.path, OpenSceneMode.Single);
// Collect from object exporters
var objExporters = UnityEngine.Object.FindObjectsByType<PSXObjectExporter>(FindObjectsSortMode.None);
foreach (var objExporter in objExporters)
{
if (objExporter.LuaFile != null && !luaFileMap.ContainsKey(objExporter.LuaFile))
{
string name = $"script_{luaFileMap.Count}";
luaFileMap[objExporter.LuaFile] = name;
}
}
// Collect scene-level Lua file
var sceneExporter = UnityEngine.Object.FindFirstObjectByType<PSXSceneExporter>();
if (sceneExporter != null && sceneExporter.SceneLuaFile != null &&
!luaFileMap.ContainsKey(sceneExporter.SceneLuaFile))
{
string name = $"script_{luaFileMap.Count}";
luaFileMap[sceneExporter.SceneLuaFile] = name;
}
// Collect from trigger boxes
var triggerBoxes = UnityEngine.Object.FindObjectsByType<PSXTriggerBox>(FindObjectsSortMode.None);
foreach (var tb in triggerBoxes)
{
if (tb.LuaFile != null && !luaFileMap.ContainsKey(tb.LuaFile))
{
string name = $"script_{luaFileMap.Count}";
luaFileMap[tb.LuaFile] = name;
}
}
}
catch (Exception ex)
{
Log($"Error scanning '{scene.name}' for Lua files: {ex.Message}", LogType.Warning);
}
}
// Write source files to disk
foreach (var kvp in luaFileMap)
{
string srcPath = Path.Combine(luaSrcDir, kvp.Value + ".lua");
File.WriteAllText(srcPath, kvp.Key.LuaScript, new System.Text.UTF8Encoding(false));
}
// Restore original scene
if (!string.IsNullOrEmpty(currentScenePath))
EditorSceneManager.OpenScene(currentScenePath, OpenSceneMode.Single);
return luaFileMap;
}
/// <summary>
/// Compile Lua source files to PS1 bytecode using luac_psx inside PCSX-Redux.
/// Returns a dictionary mapping LuaFile assets to their compiled bytecode,
/// or null on failure.
/// </summary>
private async Task<Dictionary<string, byte[]>> CompileLuaAsync(Dictionary<LuaFile, string> luaFileMap)
{
string luaSrcDir = SplashBuildPaths.LuaSrcDir;
string nativeDir = SplashBuildPaths.NativeSourceDir;
// Build luac_psx if needed
string luacDir = SplashBuildPaths.LuacPsxDir;
string luacExe = SplashBuildPaths.LuacPsxExePath;
if (!File.Exists(luacExe))
{
Log("Building Lua compiler (luac_psx)...", LogType.Log);
if (!await BuildLuacPsxAsync(luacDir))
{
Log("Failed to build luac_psx.", LogType.Error);
return null;
}
}
// Generate manifest.txt
string manifestPath = SplashBuildPaths.LuaManifestPath;
using (var sw = new StreamWriter(manifestPath, false, new System.Text.UTF8Encoding(false)))
{
foreach (var kvp in luaFileMap)
{
sw.WriteLine(kvp.Value + ".lua");
sw.WriteLine(kvp.Value + ".luac");
}
}
// Clean up previous sentinel
string sentinelPath = SplashBuildPaths.LuaDoneSentinel;
if (File.Exists(sentinelPath))
File.Delete(sentinelPath);
// Launch PCSX-Redux headless with luac_psx
string reduxBinary = SplashBuildPaths.PCSXReduxBinary;
if (!File.Exists(reduxBinary))
{
Log("PCSX-Redux not found. Install it via the Toolchain section.", LogType.Error);
return null;
}
string args = $"--no-ui --run --fastboot --pcdrv --stdout --pcdrvbase \"{luaSrcDir}\" --loadexe \"{luacExe}\"";
Log($"luac_psx: {luacExe}", LogType.Log);
Log($"pcdrvbase: {luaSrcDir}", LogType.Log);
Log($"manifest: {File.ReadAllText(manifestPath).Trim()}", LogType.Log);
var psi = new ProcessStartInfo
{
FileName = reduxBinary,
Arguments = args,
WorkingDirectory = luaSrcDir,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
Process process = null;
try
{
process = new Process { StartInfo = psi, EnableRaisingEvents = true };
var stdoutBuf = new System.Text.StringBuilder();
var stderrBuf = new System.Text.StringBuilder();
process.OutputDataReceived += (s, e) =>
{
if (e.Data != null)
{
stdoutBuf.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (s, e) =>
{
if (e.Data != null)
{
stderrBuf.AppendLine(e.Data);
}
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
// Poll for sentinel file (100ms interval, 30s timeout)
bool done = false;
int elapsed = 0;
const int timeoutMs = 30000;
const int pollMs = 100;
while (elapsed < timeoutMs)
{
await Task.Delay(pollMs);
elapsed += pollMs;
if (File.Exists(sentinelPath))
{
done = true;
break;
}
// Check if process died unexpectedly
if (process.HasExited)
{
Log("PCSX-Redux exited unexpectedly during Lua compilation.", LogType.Error);
break;
}
}
// Kill emulator
if (!process.HasExited)
{
try { process.Kill(); } catch { }
}
if (!done)
{
Log("Lua compilation timed out (30s).", LogType.Error);
return null;
}
// Check sentinel content
// Give a tiny delay for file to flush on Windows
await Task.Delay(200);
string sentinelContent = File.ReadAllText(sentinelPath).Trim();
if (string.IsNullOrEmpty(sentinelContent))
sentinelContent = "(empty)";
if (sentinelContent != "OK")
{
// Sentinel contains error details from luac_psx
foreach (string errLine in sentinelContent.Split('\n'))
{
if (!string.IsNullOrWhiteSpace(errLine))
Log(errLine.TrimEnd(), LogType.Error);
}
// Dump full stdout and stderr for diagnosis
string fullStdout = stdoutBuf.ToString().Trim();
string fullStderr = stderrBuf.ToString().Trim();
if (!string.IsNullOrEmpty(fullStdout))
{
Log("--- PCSX-Redux stdout ---", LogType.Log);
foreach (string line in fullStdout.Split('\n'))
{
if (!string.IsNullOrWhiteSpace(line))
LogToPanel(line.TrimEnd(), LogType.Log);
}
}
else
{
Log("No stdout captured from PCSX-Redux.", LogType.Warning);
}
if (!string.IsNullOrEmpty(fullStderr))
{
Log("--- PCSX-Redux stderr ---", LogType.Log);
foreach (string line in fullStderr.Split('\n'))
{
if (!string.IsNullOrWhiteSpace(line))
LogToPanel(line.TrimEnd(), LogType.Log);
}
}
return null;
}
// Read compiled bytecode files, keyed by source text
var result = new Dictionary<string, byte[]>();
foreach (var kvp in luaFileMap)
{
string luacPath = Path.Combine(luaSrcDir, kvp.Value + ".luac");
if (!File.Exists(luacPath))
{
Log($"Missing compiled bytecode: {kvp.Value}.luac", LogType.Error);
return null;
}
result[kvp.Key.LuaScript] = File.ReadAllBytes(luacPath);
}
return result;
}
catch (Exception ex)
{
Log($"Lua compilation error: {ex.Message}", LogType.Error);
return null;
}
finally
{
if (process != null)
{
if (!process.HasExited)
try { process.Kill(); } catch { }
process.Dispose();
}
}
}
/// <summary>
/// Build the luac_psx PS1 compiler executable.
/// </summary>
private async Task<bool> BuildLuacPsxAsync(string luacDir)
{
int jobCount = Math.Max(1, SystemInfo.processorCount - 1);
string makeCmd = $"make -j{jobCount}";
var psi = new ProcessStartInfo
{
FileName = Application.platform == RuntimePlatform.WindowsEditor ? "cmd.exe" : "/bin/bash",
Arguments = Application.platform == RuntimePlatform.WindowsEditor
? $"/c \"cd /d \"{luacDir}\" && {makeCmd}\""
: $"-c \"cd \\\"{luacDir}\\\" && {makeCmd}\"",
WorkingDirectory = luacDir,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
try
{
var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
var stderrBuf = new System.Text.StringBuilder();
process.OutputDataReceived += (s, e) => { };
process.ErrorDataReceived += (s, e) =>
{
if (e.Data != null) stderrBuf.AppendLine(e.Data);
};
var tcs = new TaskCompletionSource<int>();
process.Exited += (s, e) => tcs.TrySetResult(process.ExitCode);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
int exitCode = await tcs.Task;
process.Dispose();
if (exitCode != 0)
{
foreach (string line in stderrBuf.ToString().Split('\n'))
{
if (!string.IsNullOrWhiteSpace(line))
LogToPanel(line.Trim(), LogType.Error);
}
return false;
}
return true;
}
catch (Exception ex)
{
Log($"Failed to build luac_psx: {ex.Message}", LogType.Error);
return false;
}
}
// ───── Step 3: Compile ───── // ───── Step 3: Compile ─────
private async void CompileOnly() private async void CompileOnly()
@@ -1266,9 +1711,20 @@ namespace SplashEdit.EditorCode
if (SplashSettings.FpsOverlay) if (SplashSettings.FpsOverlay)
buildArg += " FPSOVERLAY=1"; buildArg += " FPSOVERLAY=1";
buildArg += $" OT_SIZE={SplashSettings.OtSize} BUMP_SIZE={SplashSettings.BumpSize}";
// Use noparser Lua library when bytecode was pre-compiled
string noparserPrefix = "";
if (_luaBytecodeCompiled)
{
buildArg += " NOPARSER=1";
// Build liblua-noparser.a first
noparserPrefix = "make -C third_party/nugget/third_party/psxlua psx-noparser && ";
}
int jobCount = Math.Max(1, SystemInfo.processorCount - 1); int jobCount = Math.Max(1, SystemInfo.processorCount - 1);
string cleanPrefix = SplashSettings.CleanBuild ? "make clean && " : ""; string cleanPrefix = SplashSettings.CleanBuild ? "make clean && " : "";
string makeCmd = $"{cleanPrefix}make all -j{jobCount} {buildArg}".Trim(); string makeCmd = $"{cleanPrefix}{noparserPrefix}make all -j{jobCount} {buildArg}".Trim();
Log($"Running: {makeCmd}", LogType.Log); Log($"Running: {makeCmd}", LogType.Log);
var psi = new ProcessStartInfo var psi = new ProcessStartInfo

View File

@@ -149,6 +149,19 @@ namespace SplashEdit.EditorCode
set => EditorPrefs.SetBool(Prefix + "FpsOverlay", value); set => EditorPrefs.SetBool(Prefix + "FpsOverlay", value);
} }
// --- Renderer sizes ---
public static int OtSize
{
get => EditorPrefs.GetInt(Prefix + "OtSize", 2048 * 4);
set => EditorPrefs.SetInt(Prefix + "OtSize", value);
}
public static int BumpSize
{
get => EditorPrefs.GetInt(Prefix + "BumpSize", 8096 * 16);
set => EditorPrefs.SetInt(Prefix + "BumpSize", value);
}
// --- Export settings --- // --- Export settings ---
public static float DefaultGTEScaling public static float DefaultGTEScaling
{ {
@@ -188,7 +201,8 @@ namespace SplashEdit.EditorCode
"PCSXReduxPath", "PCSXReduxPCdrvBase", "SerialPort", "SerialBaudRate", "PCSXReduxPath", "PCSXReduxPCdrvBase", "SerialPort", "SerialBaudRate",
"ResWidth", "ResHeight", "DualBuffering", "VerticalLayout", "ResWidth", "ResHeight", "DualBuffering", "VerticalLayout",
"GTEScaling", "AutoValidate", "GTEScaling", "AutoValidate",
"LicenseFilePath", "ISOVolumeLabel" "LicenseFilePath", "ISOVolumeLabel",
"OtSize", "BumpSize"
}; };
foreach (string key in keys) foreach (string key in keys)

View File

@@ -12,6 +12,13 @@ namespace SplashEdit.RuntimeCode
/// </summary> /// </summary>
public static class PSXSceneWriter public static class PSXSceneWriter
{ {
/// <summary>
/// Pre-compiled Lua bytecode, keyed by Lua source text.
/// When populated, Write() packs bytecode instead of source text.
/// Set by the build pipeline after running luac_psx, cleared after export.
/// </summary>
public static Dictionary<string, byte[]> CompiledLuaBytecode { get; set; }
/// <summary> /// <summary>
/// All scene data needed to produce a .bin file. /// All scene data needed to produce a .bin file.
/// Populated by PSXSceneExporter before calling <see cref="Write"/>. /// Populated by PSXSceneExporter before calling <see cref="Write"/>.
@@ -239,6 +246,11 @@ namespace SplashEdit.RuntimeCode
{ {
luaOffset.PlaceholderPositions.Add(writer.BaseStream.Position); luaOffset.PlaceholderPositions.Add(writer.BaseStream.Position);
writer.Write((int)0); // placeholder writer.Write((int)0); // placeholder
// Use compiled bytecode length if available, otherwise source length
if (CompiledLuaBytecode != null && CompiledLuaBytecode.TryGetValue(luaFile.LuaScript, out byte[] bytecode))
writer.Write((uint)bytecode.Length);
else
writer.Write((uint)Encoding.UTF8.GetByteCount(luaFile.LuaScript)); writer.Write((uint)Encoding.UTF8.GetByteCount(luaFile.LuaScript));
} }
@@ -429,12 +441,26 @@ namespace SplashEdit.RuntimeCode
// Data sections // Data sections
// ══════════════════════════════════════════════════════ // ══════════════════════════════════════════════════════
// Lua data // Lua data (bytecode if compiled, source text otherwise)
int luaIdx = 0;
log?.Invoke($"CompiledLuaBytecode: {(CompiledLuaBytecode != null ? CompiledLuaBytecode.Count + " entries" : "null")}", LogType.Log);
foreach (LuaFile luaFile in luaFiles) foreach (LuaFile luaFile in luaFiles)
{ {
AlignToFourBytes(writer); AlignToFourBytes(writer);
luaOffset.DataOffsets.Add(writer.BaseStream.Position); luaOffset.DataOffsets.Add(writer.BaseStream.Position);
writer.Write(Encoding.UTF8.GetBytes(luaFile.LuaScript));
if (CompiledLuaBytecode != null && CompiledLuaBytecode.TryGetValue(luaFile.LuaScript, out byte[] bytecode))
{
log?.Invoke($" Lua [{luaIdx}]: using bytecode ({bytecode.Length} bytes)", LogType.Log);
writer.Write(bytecode);
}
else
{
byte[] srcBytes = Encoding.UTF8.GetBytes(luaFile.LuaScript);
log?.Invoke($" Lua [{luaIdx}]: FALLBACK to source ({srcBytes.Length} bytes, script hash={luaFile.LuaScript.GetHashCode()})", LogType.Warning);
writer.Write(srcBytes);
}
luaIdx++;
} }
// Mesh data // Mesh data