From 6912439ddf2b93330d8b8a2a068e40cd53326bdd Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Thu, 17 Sep 2026 00:37:10 -0400 Subject: [PATCH] Wire RCO texture property to the renderer; fix group parent positions - Expose the RCO image table as resource.texturetable[label] - Route native-object property assignment through set(BaseNativeObject) reflection so 'plane.texture = image' reaches PlaneObject.setTexture - Plane/UButton/Button/Icon draw their runtime texture in preference to the static image, and seed the texture property from the static image - Store the RCO entry's VSMX object so group children inherit parent position --- .gitignore | 1 + .../main/java/dev/quoije/umd/rco/RcoFile.java | 5 +++- .../dev/quoije/umd/rco/object/BaseObject.java | 12 ++++++++++ .../quoije/umd/rco/object/ButtonObject.java | 15 ++++++++++++ .../dev/quoije/umd/rco/object/IconObject.java | 15 ++++++++++++ .../quoije/umd/rco/object/ImageObject.java | 4 +++- .../quoije/umd/rco/object/PlaneObject.java | 15 ++++++++++++ .../quoije/umd/rco/object/UButtonObject.java | 15 ++++++++++++ .../vsmx/interpreter/VsmxNativeObject.java | 23 +++++++++++++++++++ .../quoije/umd/rco/vsmx/objects/Resource.java | 10 ++++++++ 10 files changed, 113 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index dd462e1..03edc7c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ Thumbs.db # Disc images *.iso +reference/ diff --git a/core/src/main/java/dev/quoije/umd/rco/RcoFile.java b/core/src/main/java/dev/quoije/umd/rco/RcoFile.java index a2e36f0..7acaa7c 100644 --- a/core/src/main/java/dev/quoije/umd/rco/RcoFile.java +++ b/core/src/main/java/dev/quoije/umd/rco/RcoFile.java @@ -58,6 +58,7 @@ public final class RcoFile { public final List subEntries = new ArrayList<>(); public byte[] data; public String[] texts; + public ImageObject image; public BaseObject obj; public VsmxBaseObject vsmxObject; @@ -269,7 +270,9 @@ public final class RcoFile { if (imgData != null && comp != COMPRESSION_NONE) { imgData = decompressImage(imgData, comp, sizeUnpacked); } - images.put(entryOffset, new ImageObject(imgData)); + ImageObject imageObject = new ImageObject(imgData); + images.put(entryOffset, imageObject); + entry.image = imageObject; } } case TABLE_MODEL -> { diff --git a/core/src/main/java/dev/quoije/umd/rco/object/BaseObject.java b/core/src/main/java/dev/quoije/umd/rco/object/BaseObject.java index 872d0e8..46b368b 100644 --- a/core/src/main/java/dev/quoije/umd/rco/object/BaseObject.java +++ b/core/src/main/java/dev/quoije/umd/rco/object/BaseObject.java @@ -97,6 +97,18 @@ public abstract class BaseObject extends BaseNativeObject implements IDisplay { public VsmxBaseObject createVsmxObject(VsmxInterpreter interpreter, VsmxBaseObject parent, RcoFile.Entry entry) { VsmxNativeObject object = new VsmxNativeObject(interpreter, this); setObject(object); + entry.vsmxObject = object; + + // Seed the "texture" property with this object's static image so the + // script can read or replace it. Assignment is routed through the + // native setTexture setter, when one exists. + Object staticImage = getImage(); + if (staticImage instanceof ImageObject imageObject) { + VsmxNativeObject textureObject = new VsmxNativeObject(interpreter, imageObject); + imageObject.setObject(textureObject); + object.setPropertyValue("texture", textureObject); + } + if (entry.label != null) { name = entry.label; object.setPropertyValue("name", new VsmxString(interpreter, entry.label)); diff --git a/core/src/main/java/dev/quoije/umd/rco/object/ButtonObject.java b/core/src/main/java/dev/quoije/umd/rco/object/ButtonObject.java index acf5339..611e65b 100644 --- a/core/src/main/java/dev/quoije/umd/rco/object/ButtonObject.java +++ b/core/src/main/java/dev/quoije/umd/rco/object/ButtonObject.java @@ -6,6 +6,7 @@ import dev.quoije.umd.rco.type.EventType; import dev.quoije.umd.rco.type.ImageType; import dev.quoije.umd.rco.type.IntType; import dev.quoije.umd.rco.type.TextType; +import dev.quoije.umd.rco.vsmx.objects.BaseNativeObject; /** A button object. */ public class ButtonObject extends BasePositionObject { @@ -24,11 +25,25 @@ public class ButtonObject extends BasePositionObject { @ObjectField(order = 212) public EventType onContextMenu; @ObjectField(order = 213) public IntType unknownInt40; + /** Runtime texture assigned by the script; overrides the static image. */ + private ImageObject texture; + @Override public Object getImage() { + if (texture != null) { + return texture; + } return image.getImage(); } + /** Called via the VSMX {@code texture} property setter. */ + public void setTexture(BaseNativeObject texture) { + if (texture instanceof ImageObject imageObject) { + this.texture = imageObject; + } + onDisplayUpdated(); + } + @Override public Object getFocusImage() { return focus.getImage(); diff --git a/core/src/main/java/dev/quoije/umd/rco/object/IconObject.java b/core/src/main/java/dev/quoije/umd/rco/object/IconObject.java index deb0698..7e84334 100644 --- a/core/src/main/java/dev/quoije/umd/rco/object/IconObject.java +++ b/core/src/main/java/dev/quoije/umd/rco/object/IconObject.java @@ -3,6 +3,7 @@ package dev.quoije.umd.rco.object; import dev.quoije.umd.rco.ObjectField; import dev.quoije.umd.rco.type.ImageType; +import dev.quoije.umd.rco.vsmx.objects.BaseNativeObject; /** An icon object. */ public class IconObject extends BasePositionObject { @@ -11,8 +12,22 @@ public class IconObject extends BasePositionObject { @ObjectField(order = 202) public ImageType unknownImage18; @ObjectField(order = 203) public ImageType unknownImage20; + /** Runtime texture assigned by the script; overrides the static image. */ + private ImageObject texture; + @Override public Object getImage() { + if (texture != null) { + return texture; + } return unknownImage16.getImage(); } + + /** Called via the VSMX {@code texture} property setter. */ + public void setTexture(BaseNativeObject texture) { + if (texture instanceof ImageObject imageObject) { + this.texture = imageObject; + } + onDisplayUpdated(); + } } diff --git a/core/src/main/java/dev/quoije/umd/rco/object/ImageObject.java b/core/src/main/java/dev/quoije/umd/rco/object/ImageObject.java index 9377a4a..cd9e142 100644 --- a/core/src/main/java/dev/quoije/umd/rco/object/ImageObject.java +++ b/core/src/main/java/dev/quoije/umd/rco/object/ImageObject.java @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later package dev.quoije.umd.rco.object; +import dev.quoije.umd.rco.vsmx.objects.BaseNativeObject; + /** An image entry from the RCO IMG table (raw bytes until decoded by the UI). */ -public final class ImageObject { +public class ImageObject extends BaseNativeObject { private final byte[] data; diff --git a/core/src/main/java/dev/quoije/umd/rco/object/PlaneObject.java b/core/src/main/java/dev/quoije/umd/rco/object/PlaneObject.java index 02c511b..936b0a4 100644 --- a/core/src/main/java/dev/quoije/umd/rco/object/PlaneObject.java +++ b/core/src/main/java/dev/quoije/umd/rco/object/PlaneObject.java @@ -4,6 +4,7 @@ package dev.quoije.umd.rco.object; import dev.quoije.umd.rco.ObjectField; import dev.quoije.umd.rco.type.ImageType; import dev.quoije.umd.rco.type.IntType; +import dev.quoije.umd.rco.vsmx.objects.BaseNativeObject; /** A plane (textured rectangle). */ public class PlaneObject extends BasePositionObject { @@ -11,8 +12,22 @@ public class PlaneObject extends BasePositionObject { @ObjectField(order = 201) public ImageType image; @ObjectField(order = 202) public IntType unknownInt18; + /** Runtime texture assigned by the script; overrides the static image. */ + private ImageObject texture; + @Override public Object getImage() { + if (texture != null) { + return texture; + } return image.getImage(); } + + /** Called via the VSMX {@code texture} property setter. */ + public void setTexture(BaseNativeObject texture) { + if (texture instanceof ImageObject imageObject) { + this.texture = imageObject; + } + onDisplayUpdated(); + } } diff --git a/core/src/main/java/dev/quoije/umd/rco/object/UButtonObject.java b/core/src/main/java/dev/quoije/umd/rco/object/UButtonObject.java index 4d84651..08869ca 100644 --- a/core/src/main/java/dev/quoije/umd/rco/object/UButtonObject.java +++ b/core/src/main/java/dev/quoije/umd/rco/object/UButtonObject.java @@ -5,6 +5,7 @@ import dev.quoije.umd.rco.ObjectField; import dev.quoije.umd.rco.type.EventType; import dev.quoije.umd.rco.type.ImageType; import dev.quoije.umd.rco.type.IntType; +import dev.quoije.umd.rco.vsmx.objects.BaseNativeObject; /** A user button object (navigation with onLeft/onRight/onUp/onDown events). */ public class UButtonObject extends BasePositionObject { @@ -19,11 +20,25 @@ public class UButtonObject extends BasePositionObject { @ObjectField(order = 208) public EventType onDown; @ObjectField(order = 209) public IntType unknownInt32; + /** Runtime texture assigned by the script; overrides the static image. */ + private ImageObject texture; + @Override public Object getImage() { + if (texture != null) { + return texture; + } return image.getImage(); } + /** Called via the VSMX {@code texture} property setter. */ + public void setTexture(BaseNativeObject texture) { + if (texture instanceof ImageObject imageObject) { + this.texture = imageObject; + } + onDisplayUpdated(); + } + @Override public void setFocus() { trigger(onFocusIn); diff --git a/core/src/main/java/dev/quoije/umd/rco/vsmx/interpreter/VsmxNativeObject.java b/core/src/main/java/dev/quoije/umd/rco/vsmx/interpreter/VsmxNativeObject.java index 993cffb..fce597e 100644 --- a/core/src/main/java/dev/quoije/umd/rco/vsmx/interpreter/VsmxNativeObject.java +++ b/core/src/main/java/dev/quoije/umd/rco/vsmx/interpreter/VsmxNativeObject.java @@ -3,6 +3,8 @@ package dev.quoije.umd.rco.vsmx.interpreter; import dev.quoije.umd.rco.vsmx.objects.BaseNativeObject; +import java.lang.reflect.Method; + /** A VSMX object wrapping a native Java object. */ public class VsmxNativeObject extends VsmxObject { @@ -17,6 +19,27 @@ public class VsmxNativeObject extends VsmxObject { return object; } + /** + * Assigning a native object to a property is routed through a matching + * {@code set(BaseNativeObject)} method when the wrapped object + * declares one. This is how {@code plane.texture = image} reaches + * {@code PlaneObject.setTexture}. + */ + @Override + public void setPropertyValue(String name, VsmxBaseObject value) { + if (object != null && name != null && !name.isEmpty() && value instanceof VsmxNativeObject nativeValue) { + String setterName = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1); + try { + Method setter = object.getClass().getMethod(setterName, BaseNativeObject.class); + setter.invoke(object, nativeValue.getObject()); + return; + } catch (ReflectiveOperationException e) { + // No matching setter; fall back to storing the property. + } + } + super.setPropertyValue(name, value); + } + @Override public String toString() { return object == null ? super.toString() : object.toString() + super.toString(); diff --git a/core/src/main/java/dev/quoije/umd/rco/vsmx/objects/Resource.java b/core/src/main/java/dev/quoije/umd/rco/vsmx/objects/Resource.java index 2bd7a61..2dfcd99 100644 --- a/core/src/main/java/dev/quoije/umd/rco/vsmx/objects/Resource.java +++ b/core/src/main/java/dev/quoije/umd/rco/vsmx/objects/Resource.java @@ -7,6 +7,7 @@ import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject; import dev.quoije.umd.rco.vsmx.interpreter.VsmxInterpreter; import dev.quoije.umd.rco.vsmx.interpreter.VsmxNativeObject; import dev.quoije.umd.rco.vsmx.interpreter.VsmxObject; +import dev.quoije.umd.rco.vsmx.interpreter.VsmxString; /** * The resource native object, exposing the RCO object tables to the script. @@ -50,6 +51,15 @@ public class Resource extends BaseNativeObject { private static void createObjectFromEntry(VsmxInterpreter interpreter, Display display, Controller controller, VsmxBaseObject parent, RcoFile.Entry entry) { + if (entry.image != null) { + VsmxNativeObject object = new VsmxNativeObject(interpreter, entry.image); + entry.image.setObject(object); + if (entry.label != null) { + object.setPropertyValue("name", new VsmxString(interpreter, entry.label)); + parent.setPropertyValue(entry.label, object); + } + return; + } if (entry.obj == null) { return; }