Broken UI system
This commit is contained in:
@@ -33,6 +33,12 @@ namespace SplashEdit.RuntimeCode
|
||||
public PSXCutsceneClip[] cutscenes;
|
||||
public PSXAudioSource[] audioSources;
|
||||
|
||||
// UI canvases (v13)
|
||||
public PSXCanvasData[] canvases;
|
||||
|
||||
// Custom fonts (v13, embedded in UI block)
|
||||
public PSXFontData[] fonts;
|
||||
|
||||
// Player
|
||||
public Vector3 playerPos;
|
||||
public Quaternion playerRot;
|
||||
@@ -119,7 +125,7 @@ namespace SplashEdit.RuntimeCode
|
||||
// ──────────────────────────────────────────────────────
|
||||
writer.Write('S');
|
||||
writer.Write('P');
|
||||
writer.Write((ushort)12); // version
|
||||
writer.Write((ushort)13); // version
|
||||
writer.Write((ushort)luaFiles.Count);
|
||||
writer.Write((ushort)scene.exporters.Length);
|
||||
writer.Write((ushort)0); // navmeshCount (legacy)
|
||||
@@ -219,6 +225,15 @@ namespace SplashEdit.RuntimeCode
|
||||
long cutsceneTableOffsetPos = writer.BaseStream.Position;
|
||||
writer.Write((uint)0); // cutsceneTableOffset placeholder
|
||||
|
||||
// UI canvas header (version 13, 8 bytes)
|
||||
int uiCanvasCount = scene.canvases?.Length ?? 0;
|
||||
int uiFontCount = scene.fonts?.Length ?? 0;
|
||||
writer.Write((ushort)uiCanvasCount);
|
||||
writer.Write((byte)uiFontCount); // was uiReserved low byte
|
||||
writer.Write((byte)0); // was uiReserved high byte
|
||||
long uiTableOffsetPos = writer.BaseStream.Position;
|
||||
writer.Write((uint)0); // uiTableOffset placeholder
|
||||
|
||||
// ──────────────────────────────────────────────────────
|
||||
// Lua file metadata
|
||||
// ──────────────────────────────────────────────────────
|
||||
@@ -617,6 +632,242 @@ namespace SplashEdit.RuntimeCode
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────
|
||||
// UI canvas + font data (version 13)
|
||||
// Font descriptors: 16 bytes each (before canvas data)
|
||||
// Font pixel data: raw 4bpp (after font descriptors)
|
||||
// Canvas descriptor table: 12 bytes per canvas
|
||||
// Element records: 48 bytes each
|
||||
// Name and text strings follow with offset backfill
|
||||
// ──────────────────────────────────────────────────────
|
||||
if ((uiCanvasCount > 0 && scene.canvases != null) || uiFontCount > 0)
|
||||
{
|
||||
AlignToFourBytes(writer);
|
||||
long uiTableStart = writer.BaseStream.Position;
|
||||
|
||||
// ── Font descriptors (16 bytes each) ──
|
||||
// Layout: glyphW(1) glyphH(1) vramX(2) vramY(2) textureH(2)
|
||||
// dataOffset(4) dataSize(4)
|
||||
List<long> fontDataOffsetPositions = new List<long>();
|
||||
if (scene.fonts != null)
|
||||
{
|
||||
foreach (var font in scene.fonts)
|
||||
{
|
||||
writer.Write(font.GlyphWidth); // [0]
|
||||
writer.Write(font.GlyphHeight); // [1]
|
||||
writer.Write(font.VramX); // [2-3]
|
||||
writer.Write(font.VramY); // [4-5]
|
||||
writer.Write(font.TextureHeight); // [6-7]
|
||||
fontDataOffsetPositions.Add(writer.BaseStream.Position);
|
||||
writer.Write((uint)0); // [8-11] dataOffset placeholder
|
||||
writer.Write((uint)(font.PixelData?.Length ?? 0)); // [12-15] dataSize
|
||||
}
|
||||
}
|
||||
|
||||
// ── Font pixel data (raw 4bpp) ──
|
||||
if (scene.fonts != null)
|
||||
{
|
||||
for (int fi = 0; fi < scene.fonts.Length; fi++)
|
||||
{
|
||||
var font = scene.fonts[fi];
|
||||
if (font.PixelData == null || font.PixelData.Length == 0) continue;
|
||||
|
||||
AlignToFourBytes(writer);
|
||||
long dataPos = writer.BaseStream.Position;
|
||||
writer.Write(font.PixelData);
|
||||
|
||||
// Backfill data offset
|
||||
long curPos = writer.BaseStream.Position;
|
||||
writer.Seek((int)fontDataOffsetPositions[fi], SeekOrigin.Begin);
|
||||
writer.Write((uint)dataPos);
|
||||
writer.Seek((int)curPos, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
if (scene.fonts.Length > 0)
|
||||
{
|
||||
int totalFontBytes = 0;
|
||||
foreach (var f in scene.fonts) totalFontBytes += f.PixelData?.Length ?? 0;
|
||||
log?.Invoke($"{scene.fonts.Length} custom font(s) written ({totalFontBytes} bytes 4bpp data).", LogType.Log);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Canvas descriptor table (12 bytes each) ──
|
||||
// Layout per descriptor:
|
||||
// uint32 dataOffset — offset to this canvas's element array
|
||||
// uint8 nameLen
|
||||
// uint8 sortOrder
|
||||
// uint8 elementCount
|
||||
// uint8 flags — bit 0 = startVisible
|
||||
// uint32 nameOffset — offset to null-terminated name string
|
||||
List<long> canvasDataOffsetPos = new List<long>();
|
||||
List<long> canvasNameOffsetPos = new List<long>();
|
||||
for (int ci = 0; ci < uiCanvasCount; ci++)
|
||||
{
|
||||
var cv = scene.canvases[ci];
|
||||
string cvName = cv.Name ?? "";
|
||||
if (cvName.Length > 24) cvName = cvName.Substring(0, 24);
|
||||
|
||||
canvasDataOffsetPos.Add(writer.BaseStream.Position);
|
||||
writer.Write((uint)0); // dataOffset placeholder
|
||||
writer.Write((byte)cvName.Length); // nameLen
|
||||
writer.Write((byte)cv.SortOrder); // sortOrder
|
||||
writer.Write((byte)(cv.Elements?.Length ?? 0)); // elementCount
|
||||
writer.Write((byte)(cv.StartVisible ? 0x01 : 0x00)); // flags
|
||||
canvasNameOffsetPos.Add(writer.BaseStream.Position);
|
||||
writer.Write((uint)0); // nameOffset placeholder
|
||||
}
|
||||
|
||||
// Phase 2: Write element records (56 bytes each) per canvas
|
||||
for (int ci = 0; ci < uiCanvasCount; ci++)
|
||||
{
|
||||
var cv = scene.canvases[ci];
|
||||
if (cv.Elements == null || cv.Elements.Length == 0) continue;
|
||||
|
||||
AlignToFourBytes(writer);
|
||||
long elemStart = writer.BaseStream.Position;
|
||||
|
||||
// Track text offset positions for backfill
|
||||
List<long> textOffsetPositions = new List<long>();
|
||||
List<string> textContents = new List<string>();
|
||||
|
||||
foreach (var el in cv.Elements)
|
||||
{
|
||||
// Identity (8 bytes)
|
||||
writer.Write((byte)el.Type); // type
|
||||
byte eFlags = (byte)(el.StartVisible ? 0x01 : 0x00);
|
||||
writer.Write(eFlags); // flags
|
||||
string eName = el.Name ?? "";
|
||||
if (eName.Length > 24) eName = eName.Substring(0, 24);
|
||||
writer.Write((byte)eName.Length); // nameLen
|
||||
writer.Write((byte)0); // pad0
|
||||
// nameOffset placeholder (backfilled later)
|
||||
long elemNameOffPos = writer.BaseStream.Position;
|
||||
writer.Write((uint)0); // nameOffset
|
||||
|
||||
// Layout (8 bytes)
|
||||
writer.Write(el.X);
|
||||
writer.Write(el.Y);
|
||||
writer.Write(el.W);
|
||||
writer.Write(el.H);
|
||||
|
||||
// Anchors (4 bytes)
|
||||
writer.Write(el.AnchorMinX);
|
||||
writer.Write(el.AnchorMinY);
|
||||
writer.Write(el.AnchorMaxX);
|
||||
writer.Write(el.AnchorMaxY);
|
||||
|
||||
// Primary color (4 bytes)
|
||||
writer.Write(el.ColorR);
|
||||
writer.Write(el.ColorG);
|
||||
writer.Write(el.ColorB);
|
||||
writer.Write((byte)0); // pad1
|
||||
|
||||
// Type-specific data (16 bytes)
|
||||
switch (el.Type)
|
||||
{
|
||||
case PSXUIElementType.Image:
|
||||
writer.Write(el.TexpageX); // [0]
|
||||
writer.Write(el.TexpageY); // [1]
|
||||
writer.Write(el.ClutX); // [2-3]
|
||||
writer.Write(el.ClutY); // [4-5]
|
||||
writer.Write(el.U0); // [6]
|
||||
writer.Write(el.V0); // [7]
|
||||
writer.Write(el.U1); // [8]
|
||||
writer.Write(el.V1); // [9]
|
||||
writer.Write(el.BitDepthIndex); // [10]
|
||||
writer.Write(new byte[5]); // [11-15] padding
|
||||
break;
|
||||
case PSXUIElementType.Progress:
|
||||
writer.Write(el.BgR); // [0]
|
||||
writer.Write(el.BgG); // [1]
|
||||
writer.Write(el.BgB); // [2]
|
||||
writer.Write(el.ProgressValue); // [3]
|
||||
writer.Write(new byte[12]); // [4-15] padding
|
||||
break;
|
||||
case PSXUIElementType.Text:
|
||||
writer.Write(el.FontIndex); // [0] font index (0=system, 1+=custom)
|
||||
writer.Write(new byte[15]); // [1-15] padding
|
||||
break;
|
||||
default:
|
||||
writer.Write(new byte[16]); // zeroed
|
||||
break;
|
||||
}
|
||||
|
||||
// Text content offset (8 bytes)
|
||||
long textOff = writer.BaseStream.Position;
|
||||
writer.Write((uint)0); // textOffset placeholder
|
||||
writer.Write((uint)0); // pad2
|
||||
|
||||
// Remember for backfill
|
||||
textOffsetPositions.Add(textOff);
|
||||
textContents.Add(el.Type == PSXUIElementType.Text ? (el.DefaultText ?? "") : null);
|
||||
|
||||
// Also remember element name for backfill
|
||||
// We need to write it after all elements
|
||||
textOffsetPositions.Add(elemNameOffPos);
|
||||
textContents.Add("__NAME__" + eName);
|
||||
}
|
||||
|
||||
// Backfill canvas data offset
|
||||
{
|
||||
long curPos = writer.BaseStream.Position;
|
||||
writer.Seek((int)canvasDataOffsetPos[ci], SeekOrigin.Begin);
|
||||
writer.Write((uint)elemStart);
|
||||
writer.Seek((int)curPos, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// Write strings and backfill offsets
|
||||
for (int si = 0; si < textOffsetPositions.Count; si++)
|
||||
{
|
||||
string s = textContents[si];
|
||||
if (s == null) continue;
|
||||
|
||||
bool isName = s.StartsWith("__NAME__");
|
||||
string content = isName ? s.Substring(8) : s;
|
||||
if (string.IsNullOrEmpty(content) && !isName) continue;
|
||||
|
||||
long strPos = writer.BaseStream.Position;
|
||||
byte[] strBytes = Encoding.UTF8.GetBytes(content);
|
||||
writer.Write(strBytes);
|
||||
writer.Write((byte)0); // null terminator
|
||||
|
||||
long curPos = writer.BaseStream.Position;
|
||||
writer.Seek((int)textOffsetPositions[si], SeekOrigin.Begin);
|
||||
writer.Write((uint)strPos);
|
||||
writer.Seek((int)curPos, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
// Write canvas name strings and backfill name offsets
|
||||
for (int ci = 0; ci < uiCanvasCount; ci++)
|
||||
{
|
||||
string cvName = scene.canvases[ci].Name ?? "";
|
||||
if (cvName.Length > 24) cvName = cvName.Substring(0, 24);
|
||||
|
||||
long namePos = writer.BaseStream.Position;
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(cvName);
|
||||
writer.Write(nameBytes);
|
||||
writer.Write((byte)0); // null terminator
|
||||
|
||||
long curPos = writer.BaseStream.Position;
|
||||
writer.Seek((int)canvasNameOffsetPos[ci], SeekOrigin.Begin);
|
||||
writer.Write((uint)namePos);
|
||||
writer.Seek((int)curPos, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// Backfill UI table offset in header
|
||||
{
|
||||
long curPos = writer.BaseStream.Position;
|
||||
writer.Seek((int)uiTableOffsetPos, SeekOrigin.Begin);
|
||||
writer.Write((uint)uiTableStart);
|
||||
writer.Seek((int)curPos, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
int totalElements = 0;
|
||||
foreach (var cv in scene.canvases) totalElements += cv.Elements?.Length ?? 0;
|
||||
log?.Invoke($"{uiCanvasCount} UI canvases ({totalElements} elements) written.", LogType.Log);
|
||||
}
|
||||
|
||||
// Backfill offsets
|
||||
BackfillOffsets(writer, luaOffset, "lua", log);
|
||||
BackfillOffsets(writer, meshOffset, "mesh", log);
|
||||
|
||||
Reference in New Issue
Block a user