Add RCO object model and expose the menu object tree

- Type system: BaseType (+Int/Float/Unknown) and BaseReferenceType
  (+Event/Image/Text/Ref/Object/Model) for typed record fields.
- Object model: BaseObject/BasePositionObject and all 25 object classes
  (Page, Plane, Button, UButton, Text, Model, Icon, Group, Action,
  XMenu/XMList/XList/XItem, MList/MItem, LList/LItem, IList/IItem,
  Progress, Scroll, Spin, ItemSpin, Edit, Clock) with reflection-based
  field deserialization.
- ObjectFactory + RcoContext materialize 89 objects and 81 images from
  the RCO; RcoFile resolves references and wires entry.obj/vsmxObject.
- Resource builds the live pagetable/animtable/soundtable/texturetable
  into the VSMX tree; Controller tracks input focus.
- Validated on Batman Begins: onAutoPlay still drives playback
  (stream 00003) with the object tree populated.
This commit is contained in:
2026-09-15 22:54:46 -04:00
parent e1b8d09844
commit b18e8447dc
32 changed files with 961 additions and 11 deletions
@@ -406,7 +406,8 @@ public final class Main {
} }
RcoFile rco = new RcoFile(data); RcoFile rco = new RcoFile(data);
System.out.printf("%s: valid=%b compression=%d%n", rcoPath, rco.isValid(), rco.compression()); System.out.printf("%s: valid=%b compression=%d objects=%d images=%d%n",
rcoPath, rco.isValid(), rco.compression(), rco.objects().size(), rco.images().size());
System.out.printf("events (%d):%n", rco.events().size()); System.out.printf("events (%d):%n", rco.events().size());
for (Map.Entry<Integer, String> event : rco.events().entrySet()) { for (Map.Entry<Integer, String> event : rco.events().entrySet()) {
@@ -466,7 +467,7 @@ public final class Main {
VsmxNativeObject globals = GlobalVariables.create(interpreter, 854, 480); VsmxNativeObject globals = GlobalVariables.create(interpreter, 854, 480);
VsmxNativeObject controller = Controller.create(interpreter, menuHost, resourceName); VsmxNativeObject controller = Controller.create(interpreter, menuHost, resourceName);
VsmxNativeObject movieplayer = MoviePlayer.create(interpreter, menuHost, controller); VsmxNativeObject movieplayer = MoviePlayer.create(interpreter, menuHost, controller);
VsmxNativeObject resource = Resource.create(interpreter); VsmxNativeObject resource = Resource.create(interpreter, null, controller, rco);
VsmxNativeObject math = dev.quoije.umd.rco.vsmx.objects.Math.create(interpreter); VsmxNativeObject math = dev.quoije.umd.rco.vsmx.objects.Math.create(interpreter);
globals.setPropertyValue(Controller.objectName, controller); globals.setPropertyValue(Controller.objectName, controller);
globals.setPropertyValue(MoviePlayer.objectName, movieplayer); globals.setPropertyValue(MoviePlayer.objectName, movieplayer);
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco;
import dev.quoije.umd.rco.object.ActionObject;
import dev.quoije.umd.rco.object.BaseObject;
import dev.quoije.umd.rco.object.ButtonObject;
import dev.quoije.umd.rco.object.ClockObject;
import dev.quoije.umd.rco.object.EditObject;
import dev.quoije.umd.rco.object.GroupObject;
import dev.quoije.umd.rco.object.IItemObject;
import dev.quoije.umd.rco.object.IListObject;
import dev.quoije.umd.rco.object.IconObject;
import dev.quoije.umd.rco.object.ItemSpinObject;
import dev.quoije.umd.rco.object.LItemObject;
import dev.quoije.umd.rco.object.LListObject;
import dev.quoije.umd.rco.object.MItemObject;
import dev.quoije.umd.rco.object.MListObject;
import dev.quoije.umd.rco.object.ModelObject;
import dev.quoije.umd.rco.object.PageObject;
import dev.quoije.umd.rco.object.PlaneObject;
import dev.quoije.umd.rco.object.ProgressObject;
import dev.quoije.umd.rco.object.ScrollObject;
import dev.quoije.umd.rco.object.SpinObject;
import dev.quoije.umd.rco.object.TextObject;
import dev.quoije.umd.rco.object.UButtonObject;
import dev.quoije.umd.rco.object.XItemObject;
import dev.quoije.umd.rco.object.XListObject;
import dev.quoije.umd.rco.object.XMenuObject;
import dev.quoije.umd.rco.object.XMListObject;
/** Creates RCO object records by type id. */
public final class ObjectFactory {
private ObjectFactory() {
}
public static BaseObject newObject(int type) {
return switch (type) {
case 1 -> new PageObject();
case 2 -> new PlaneObject();
case 3 -> new ButtonObject();
case 4 -> new XMenuObject();
case 5 -> new XMListObject();
case 6 -> new XListObject();
case 7 -> new ProgressObject();
case 8 -> new ScrollObject();
case 9 -> new MListObject();
case 10 -> new MItemObject();
case 12 -> new XItemObject();
case 13 -> new TextObject();
case 14 -> new ModelObject();
case 15 -> new SpinObject();
case 16 -> new ActionObject();
case 17 -> new ItemSpinObject();
case 18 -> new GroupObject();
case 19 -> new LListObject();
case 20 -> new LItemObject();
case 21 -> new EditObject();
case 22 -> new ClockObject();
case 23 -> new IListObject();
case 24 -> new IItemObject();
case 25 -> new IconObject();
case 26 -> new UButtonObject();
default -> null;
};
}
}
@@ -1,7 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco; package dev.quoije.umd.rco;
import dev.quoije.umd.rco.object.BaseObject;
import dev.quoije.umd.rco.object.ImageObject;
import dev.quoije.umd.rco.vsmx.Vsmx; import dev.quoije.umd.rco.vsmx.Vsmx;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
@@ -55,6 +58,8 @@ public final class RcoFile {
public final List<Entry> subEntries = new ArrayList<>(); public final List<Entry> subEntries = new ArrayList<>();
public byte[] data; public byte[] data;
public String[] texts; public String[] texts;
public BaseObject obj;
public VsmxBaseObject vsmxObject;
public String tableName() { public String tableName() {
return RcoFile.tableName(id); return RcoFile.tableName(id);
@@ -66,6 +71,8 @@ public final class RcoFile {
private final Entry mainTable; private final Entry mainTable;
private final byte[] vsmxData; private final byte[] vsmxData;
private final Map<Integer, String> events = new LinkedHashMap<>(); private final Map<Integer, String> events = new LinkedHashMap<>();
private final Map<Integer, ImageObject> images = new LinkedHashMap<>();
private final Map<Integer, BaseObject> objects = new LinkedHashMap<>();
private final byte[] buffer; private final byte[] buffer;
private int offset; private int offset;
@@ -95,6 +102,11 @@ public final class RcoFile {
.map(e -> e.data) .map(e -> e.data)
.findFirst() .findFirst()
.orElse(null); .orElse(null);
RcoContext initContext = new RcoContext(null, 0, events, images, objects);
for (BaseObject object : objects.values()) {
object.init(initContext);
}
ok = true; ok = true;
} catch (RuntimeException e) { } catch (RuntimeException e) {
ok = false; ok = false;
@@ -126,6 +138,14 @@ public final class RcoFile {
return events; return events;
} }
public Map<Integer, ImageObject> images() {
return images;
}
public Map<Integer, BaseObject> objects() {
return objects;
}
public static String tableName(int id) { public static String tableName(int id) {
return switch (id) { return switch (id) {
case TABLE_MAIN -> "MAIN"; case TABLE_MAIN -> "MAIN";
@@ -221,11 +241,23 @@ public final class RcoFile {
skip(alignUp(lengthVSMX, 4) - lengthVSMX); skip(alignUp(lengthVSMX, 4) - lengthVSMX);
} }
} }
case TABLE_IMG, TABLE_MODEL -> { case TABLE_IMG -> {
if (entry.type == 1) { if (entry.type == 1) {
skip(2); // format skip(2); // format
int comp = read16(); int comp = read16();
int sizePacked = read32(); skip(4); // sizePacked
skip(4); // offset
if (comp != COMPRESSION_NONE) {
skip(4); // sizeUnpacked
}
images.put(entryOffset, new ImageObject(null));
}
}
case TABLE_MODEL -> {
if (entry.type == 1) {
skip(2); // format
int comp = read16();
skip(4); // sizePacked
skip(4); // offset skip(4); // offset
if (comp != COMPRESSION_NONE) { if (comp != COMPRESSION_NONE) {
skip(4); // sizeUnpacked skip(4); // sizeUnpacked
@@ -253,6 +285,12 @@ public final class RcoFile {
int dataLength = entryDataLength(entry, RcoEntrySizes.objectSize(entry.type)); int dataLength = entryDataLength(entry, RcoEntrySizes.objectSize(entry.type));
if (dataLength > 0) { if (dataLength > 0) {
entry.data = readBytes(dataLength); entry.data = readBytes(dataLength);
BaseObject obj = ObjectFactory.newObject(entry.type);
if (obj != null) {
obj.read(new RcoContext(entry.data, 0, events, images, objects));
objects.put(entryOffset, obj);
entry.obj = obj;
}
} }
} }
case TABLE_ANIM -> { case TABLE_ANIM -> {
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.RefType;
/** An action object. */
public class ActionObject extends BaseObject {
@ObjectField(order = 1) public RefType unknownRef0;
}
@@ -2,6 +2,7 @@
package dev.quoije.umd.rco.object; package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.Display; import dev.quoije.umd.rco.Display;
import dev.quoije.umd.rco.IDisplay;
import dev.quoije.umd.rco.ObjectField; import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.RcoContext; import dev.quoije.umd.rco.RcoContext;
import dev.quoije.umd.rco.RcoFile; import dev.quoije.umd.rco.RcoFile;
@@ -133,7 +134,8 @@ public abstract class BaseObject extends BaseNativeObject implements IDisplay {
} }
} }
protected void onDisplayUpdated() { @Override
public void onDisplayUpdated() {
if (display != null) { if (display != null) {
display.repaint(); display.repaint();
} }
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.FloatType;
import dev.quoije.umd.rco.type.IntType;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxArray;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxInterpreter;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxNumber;
/**
* Base of positionable RCO objects: adds position, scale, colour, and size
* fields plus script-facing accessors.
*/
public class BasePositionObject extends BaseObject {
@ObjectField(order = 101) public FloatType posX;
@ObjectField(order = 102) public FloatType posY;
@ObjectField(order = 103) public FloatType posZ;
@ObjectField(order = 104) public FloatType redScale;
@ObjectField(order = 105) public FloatType greenScale;
@ObjectField(order = 106) public FloatType blueScale;
@ObjectField(order = 107) public FloatType alphaScale;
@ObjectField(order = 108) public FloatType width;
@ObjectField(order = 109) public FloatType height;
@ObjectField(order = 110) public FloatType depth;
@ObjectField(order = 111) public FloatType scaleWidth;
@ObjectField(order = 112) public FloatType scaleHeight;
@ObjectField(order = 113) public FloatType scaleDepth;
@ObjectField(order = 114) public IntType iconOffset;
@ObjectField(order = 115) public EventType onInit;
@Override
public int getX() {
int parentX = 0;
if (getParent() instanceof BasePositionObject positionParent) {
parentX = positionParent.getX();
}
return parentX + posX.getIntValue();
}
@Override
public int getY() {
int parentY = 0;
if (getParent() instanceof BasePositionObject positionParent) {
parentY = positionParent.getY();
}
return parentY + posY.getIntValue();
}
@Override
public int getWidth() {
float w = width.getFloatValue();
if (w == 0f) {
Object image = getImage();
if (image == null) {
w = 0f;
}
}
return Math.round(w * scaleWidth.getFloatValue());
}
@Override
public int getHeight() {
float h = height.getFloatValue();
if (h == 0f) {
Object image = getImage();
if (image == null) {
h = 0f;
}
}
return Math.round(h * scaleHeight.getFloatValue());
}
@Override
public float getAlpha() {
return alphaScale.getFloatValue();
}
@Override
public Object getImage() {
return null;
}
@Override
public void setFocus() {
if (display != null) {
display.setFocus(this);
}
if (controller != null) {
controller.setFocus(this);
}
}
public void setPos(VsmxBaseObject object, VsmxBaseObject x, VsmxBaseObject y) {
posX.setFloatValue(x.getFloatValue());
posY.setFloatValue(y.getFloatValue());
onDisplayUpdated();
}
public void setPos(VsmxBaseObject object, VsmxBaseObject x, VsmxBaseObject y, VsmxBaseObject z) {
posX.setFloatValue(x.getFloatValue());
posY.setFloatValue(y.getFloatValue());
posZ.setFloatValue(z.getFloatValue());
onDisplayUpdated();
}
public VsmxBaseObject getPos(VsmxBaseObject object) {
VsmxInterpreter interpreter = object.getInterpreter();
VsmxArray pos = new VsmxArray(interpreter, 3);
pos.setPropertyValue(0, new VsmxNumber(interpreter, posX.getFloatValue()));
pos.setPropertyValue(1, new VsmxNumber(interpreter, posY.getFloatValue()));
pos.setPropertyValue(2, new VsmxNumber(interpreter, posZ.getFloatValue()));
return pos;
}
public void setSize(VsmxBaseObject object, VsmxBaseObject w, VsmxBaseObject h, VsmxBaseObject d) {
width.setFloatValue(w.getFloatValue());
height.setFloatValue(h.getFloatValue());
depth.setFloatValue(d.getFloatValue());
onDisplayUpdated();
}
public void setScale(VsmxBaseObject object, VsmxBaseObject w, VsmxBaseObject h, VsmxBaseObject d) {
onDisplayUpdated();
}
public VsmxBaseObject getColor(VsmxBaseObject object) {
VsmxInterpreter interpreter = object.getInterpreter();
VsmxArray color = new VsmxArray(interpreter, 4);
color.setPropertyValue(0, new VsmxNumber(interpreter, redScale.getFloatValue()));
color.setPropertyValue(1, new VsmxNumber(interpreter, greenScale.getFloatValue()));
color.setPropertyValue(2, new VsmxNumber(interpreter, blueScale.getFloatValue()));
color.setPropertyValue(3, new VsmxNumber(interpreter, alphaScale.getFloatValue()));
return color;
}
public void setColor(VsmxBaseObject object, VsmxBaseObject red, VsmxBaseObject green, VsmxBaseObject blue, VsmxBaseObject alpha) {
redScale.setFloatValue(red.getFloatValue());
greenScale.setFloatValue(green.getFloatValue());
blueScale.setFloatValue(blue.getFloatValue());
alphaScale.setFloatValue(alpha.getFloatValue());
onDisplayUpdated();
}
public void setRotate(VsmxBaseObject object, VsmxBaseObject x, VsmxBaseObject y, VsmxBaseObject rotationRads) {
onDisplayUpdated();
}
public void animColor(VsmxBaseObject object, VsmxBaseObject red, VsmxBaseObject green, VsmxBaseObject blue, VsmxBaseObject alpha, VsmxBaseObject duration) {
setColor(object, red, green, blue, alpha);
}
public void animScale(VsmxBaseObject object, VsmxBaseObject w, VsmxBaseObject h, VsmxBaseObject d, VsmxBaseObject duration) {
setScale(object, w, h, d);
}
public void animPos(VsmxBaseObject object, VsmxBaseObject x, VsmxBaseObject y, VsmxBaseObject z, VsmxBaseObject duration) {
setPos(object, x, y, z);
}
public void animRotate(VsmxBaseObject object, VsmxBaseObject x, VsmxBaseObject y, VsmxBaseObject rotationRads, VsmxBaseObject duration) {
setRotate(object, x, y, rotationRads);
}
}
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
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.type.TextType;
/** A button object. */
public class ButtonObject extends BasePositionObject {
@ObjectField(order = 201) public ImageType image;
@ObjectField(order = 202) public ImageType shadow;
@ObjectField(order = 203) public ImageType focus;
@ObjectField(order = 204) public TextType text;
@ObjectField(order = 205) public EventType onPush;
@ObjectField(order = 206) public EventType onFocusIn;
@ObjectField(order = 207) public EventType onFocusOut;
@ObjectField(order = 208) public EventType onFocusLeft;
@ObjectField(order = 209) public EventType onFocusRight;
@ObjectField(order = 210) public EventType onFocusUp;
@ObjectField(order = 211) public EventType onFocusDown;
@ObjectField(order = 212) public EventType onContextMenu;
@ObjectField(order = 213) public IntType unknownInt40;
@Override
public Object getImage() {
return image.getImage() == null ? null : image.getImage().getImage();
}
@Override
public void setFocus() {
trigger(onFocusIn);
super.setFocus();
}
@Override
public void focusOut() {
trigger(onFocusOut);
super.focusOut();
}
@Override
public void onPush() {
trigger(onPush);
}
@Override
public void onUp() {
trigger(onFocusUp);
}
@Override
public void onDown() {
trigger(onFocusDown);
}
@Override
public void onLeft() {
trigger(onFocusLeft);
}
@Override
public void onRight() {
trigger(onFocusRight);
}
}
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.FloatType;
import dev.quoije.umd.rco.type.IntType;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.TextType;
/** A clock object. */
public class ClockObject extends BasePositionObject {
@ObjectField(order = 201) public IntType unknownInt16;
@ObjectField(order = 202) public FloatType unknownFloat17;
@ObjectField(order = 203) public TextType unknownText18;
@ObjectField(order = 204) public TextType unknownText20;
@ObjectField(order = 205) public RefType unknownRef22;
@ObjectField(order = 206) public RefType unknownRef24;
@ObjectField(order = 207) public EventType onDecide;
@ObjectField(order = 208) public EventType onCancel;
@ObjectField(order = 209) public EventType onFocusIn;
@ObjectField(order = 210) public EventType onFocusOut;
@ObjectField(order = 211) public EventType onFocusLeft;
@ObjectField(order = 212) public EventType onFocusRight;
@ObjectField(order = 213) public EventType onFocusUp;
@ObjectField(order = 214) public EventType onFocusDown;
@ObjectField(order = 215) public EventType onDigitalSwitch;
}
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.ObjectType;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.UnknownType;
/** An edit field object. */
public class EditObject extends BasePositionObject {
@ObjectField(order = 201) public UnknownType unknown16;
@ObjectField(order = 202) public UnknownType unknown17;
@ObjectField(order = 203) public UnknownType unknown18;
@ObjectField(order = 204) public UnknownType unknown19;
@ObjectField(order = 205) public RefType unknownRef20;
@ObjectField(order = 206) public RefType unknownRef22;
@ObjectField(order = 207) public EventType unknownEvent24;
@ObjectField(order = 208) public RefType unknownRef26;
@ObjectField(order = 209) public RefType unknownRef28;
@ObjectField(order = 210) public RefType unknownRef30;
@ObjectField(order = 211) public EventType unknownEvent32;
@ObjectField(order = 212) public EventType unknownEvent34;
@ObjectField(order = 213) public ObjectType unknownObject36;
@ObjectField(order = 214) public ObjectType unknownObject38;
@ObjectField(order = 215) public RefType unknownRef40;
}
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
/** A group object (position container with no extra fields). */
public class GroupObject extends BasePositionObject {
}
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.TextType;
/** An input list item. */
public class IItemObject extends BaseObject {
@ObjectField(order = 1) public TextType textDefault;
@ObjectField(order = 2) public TextType textError;
}
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.FloatType;
/** An input list object. */
public class IListObject extends BasePositionObject {
@ObjectField(order = 201) public FloatType unknownFloat16;
@ObjectField(order = 202) public EventType onFocusIn;
@ObjectField(order = 203) public EventType onFocusOut;
@ObjectField(order = 204) public EventType onFocusLeft;
@ObjectField(order = 205) public EventType onFocusRight;
}
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.ImageType;
/** An icon object. */
public class IconObject extends BasePositionObject {
@ObjectField(order = 201) public ImageType unknownImage16;
@ObjectField(order = 202) public ImageType unknownImage18;
@ObjectField(order = 203) public ImageType unknownImage20;
@Override
public Object getImage() {
return unknownImage16.getImage() == null ? null : unknownImage16.getImage().getImage();
}
}
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.FloatType;
import dev.quoije.umd.rco.type.IntType;
import dev.quoije.umd.rco.type.ObjectType;
import dev.quoije.umd.rco.type.RefType;
/** An item spin object. */
public class ItemSpinObject extends BasePositionObject {
@ObjectField(order = 201) public IntType unknownInt16;
@ObjectField(order = 202) public IntType unknownInt17;
@ObjectField(order = 203) public IntType unknownInt18;
@ObjectField(order = 204) public IntType unknownInt19;
@ObjectField(order = 205) public IntType unknownInt20;
@ObjectField(order = 206) public FloatType unknownFloat21;
@ObjectField(order = 207) public RefType unknownRef22;
@ObjectField(order = 208) public RefType unknownRef24;
@ObjectField(order = 209) public EventType unknownEvent26;
@ObjectField(order = 210) public EventType unknownEvent28;
@ObjectField(order = 211) public RefType unknownRef30;
@ObjectField(order = 212) public RefType unknownRef32;
@ObjectField(order = 213) public RefType unknownRef34;
@ObjectField(order = 214) public RefType unknownRef36;
@ObjectField(order = 215) public ObjectType objPrev;
@ObjectField(order = 216) public ObjectType objNext;
}
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.TextType;
/** A list item (variant). */
public class LItemObject extends BaseObject {
@ObjectField(order = 1) public TextType text;
@ObjectField(order = 2) public RefType unknownRef2;
@ObjectField(order = 3) public RefType unknownRef4;
}
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.FloatType;
import dev.quoije.umd.rco.type.IntType;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.UnknownType;
/** A list object (variant). */
public class LListObject extends BasePositionObject {
@ObjectField(order = 201) public IntType unknownInt16;
@ObjectField(order = 202) public UnknownType unknown17;
@ObjectField(order = 203) public FloatType unknownFloat18;
@ObjectField(order = 204) public RefType unknownRef19;
@ObjectField(order = 205) public RefType unknownRef21;
@ObjectField(order = 206) public RefType unknownRef23;
@ObjectField(order = 207) public RefType unknownRef25;
@ObjectField(order = 208) public RefType unknownRef27;
@ObjectField(order = 209) public RefType unknownRef29;
@ObjectField(order = 210) public EventType unknownEvent31;
@ObjectField(order = 211) public RefType unknownRef33;
}
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.TextType;
/** A list item. */
public class MItemObject extends BaseObject {
@ObjectField(order = 1) public TextType text;
@ObjectField(order = 2) public TextType altText;
@ObjectField(order = 3) public RefType unknownRef4;
}
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.FloatType;
import dev.quoije.umd.rco.type.IntType;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.UnknownType;
/** A list object. */
public class MListObject extends BasePositionObject {
@ObjectField(order = 201) public IntType numVisibleElements;
@ObjectField(order = 202) public UnknownType unknown17;
@ObjectField(order = 203) public IntType initTopPadding;
@ObjectField(order = 204) public FloatType unknownFloat19;
@ObjectField(order = 205) public FloatType itemSpacing;
@ObjectField(order = 206) public RefType unknownRef21;
@ObjectField(order = 207) public EventType onPush;
@ObjectField(order = 208) public EventType onCursorMove;
@ObjectField(order = 209) public EventType onFocusIn;
@ObjectField(order = 210) public EventType onFocusOut;
@ObjectField(order = 211) public EventType onFocusLeft;
@ObjectField(order = 212) public EventType onFocusRight;
@ObjectField(order = 213) public EventType onFocusUp;
@ObjectField(order = 214) public EventType onFocusDown;
@ObjectField(order = 215) public EventType onScrollIn;
@ObjectField(order = 216) public EventType onScrollOut;
}
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.ModelType;
/** A 3D model object. */
public class ModelObject extends BasePositionObject {
@ObjectField(order = 201) public ModelType model;
}
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.IntType;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
/** A menu page. */
public class PageObject extends BaseObject {
@ObjectField(order = 1) public IntType unknownInt0;
@ObjectField(order = 2) public EventType onInit;
@ObjectField(order = 3) public EventType onCancel;
@ObjectField(order = 4) public EventType onContextMenu;
@ObjectField(order = 5) public EventType onActivate;
public VsmxBaseObject open(VsmxBaseObject object) {
trigger(onInit);
return object;
}
public VsmxBaseObject activate(VsmxBaseObject object) {
trigger(onActivate);
return object;
}
public void close(VsmxBaseObject object) {
trigger(onCancel);
}
}
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-or-later
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;
/** A plane (textured rectangle). */
public class PlaneObject extends BasePositionObject {
@ObjectField(order = 201) public ImageType image;
@ObjectField(order = 202) public IntType unknownInt18;
@Override
public Object getImage() {
return image.getImage() == null ? null : image.getImage().getImage();
}
}
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.FloatType;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.UnknownType;
/** A progress bar object. */
public class ProgressObject extends BasePositionObject {
@ObjectField(order = 201) public FloatType unknownFloat16;
@ObjectField(order = 202) public UnknownType unknown17;
@ObjectField(order = 203) public RefType unknownRef18;
@ObjectField(order = 204) public RefType unknownRef20;
@ObjectField(order = 205) public RefType unknownRef22;
}
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.FloatType;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.UnknownType;
/** A scroll bar object. */
public class ScrollObject extends BasePositionObject {
@ObjectField(order = 201) public FloatType unknownFloat16;
@ObjectField(order = 202) public FloatType unknownFloat17;
@ObjectField(order = 203) public UnknownType unknown18;
@ObjectField(order = 204) public RefType unknownRef19;
@ObjectField(order = 205) public RefType unknownRef21;
@ObjectField(order = 206) public RefType unknownRef23;
@ObjectField(order = 207) public RefType unknownRef25;
@ObjectField(order = 208) public RefType unknownRef27;
}
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.IntType;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.UnknownType;
/** A spin (numeric stepper) object. */
public class SpinObject extends BasePositionObject {
@ObjectField(order = 201) public UnknownType unknown16;
@ObjectField(order = 202) public IntType unknownInt17;
@ObjectField(order = 203) public RefType unknownRef18;
@ObjectField(order = 204) public RefType unknownRef20;
@ObjectField(order = 205) public EventType unknownEvent22;
@ObjectField(order = 206) public EventType unknownEvent24;
@ObjectField(order = 207) public EventType unknownEvent26;
@ObjectField(order = 208) public RefType unknownRef28;
@ObjectField(order = 209) public RefType unknownRef30;
@ObjectField(order = 210) public RefType unknownRef32;
@ObjectField(order = 211) public RefType unknownRef34;
@ObjectField(order = 212) public RefType unknownRef36;
}
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.FloatType;
import dev.quoije.umd.rco.type.IntType;
import dev.quoije.umd.rco.type.RefType;
import dev.quoije.umd.rco.type.TextType;
import dev.quoije.umd.rco.type.UnknownType;
/** A text object. */
public class TextObject extends BasePositionObject {
@ObjectField(order = 201) public TextType text;
@ObjectField(order = 202) public RefType unknownRef18;
@ObjectField(order = 203) public IntType unknownInt20;
@ObjectField(order = 204) public IntType unknownInt21;
@ObjectField(order = 205) public FloatType size;
@ObjectField(order = 206) public FloatType topRed;
@ObjectField(order = 207) public FloatType topGreen;
@ObjectField(order = 208) public FloatType topBlue;
@ObjectField(order = 209) public FloatType bottomRed;
@ObjectField(order = 210) public FloatType bottomGreen;
@ObjectField(order = 211) public FloatType bottomBlue;
@ObjectField(order = 212) public FloatType spacingHorizontal;
@ObjectField(order = 213) public UnknownType unknown30;
@ObjectField(order = 214) public UnknownType unknown31;
@ObjectField(order = 215) public UnknownType unknown32;
@ObjectField(order = 216) public FloatType spacingVertical;
@ObjectField(order = 217) public FloatType shadowX;
@ObjectField(order = 218) public FloatType shadowY;
@ObjectField(order = 219) public FloatType shadowPerspective;
@ObjectField(order = 220) public FloatType shadowRed;
@ObjectField(order = 221) public FloatType shadowGreen;
@ObjectField(order = 222) public FloatType shadowBlue;
@ObjectField(order = 223) public FloatType shadowAlpha;
@ObjectField(order = 224) public UnknownType unknown41;
@ObjectField(order = 225) public UnknownType unknown42;
@ObjectField(order = 226) public UnknownType unknown43;
@ObjectField(order = 227) public FloatType unknownFloat44;
@ObjectField(order = 228) public FloatType unknownFloat45;
@ObjectField(order = 229) public FloatType unknownFloat46;
@ObjectField(order = 230) public FloatType unknownFloat47;
@ObjectField(order = 231) public UnknownType unknown48;
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
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;
/** A user button object (navigation with onLeft/onRight/onUp/onDown events). */
public class UButtonObject extends BasePositionObject {
@ObjectField(order = 201) public ImageType image;
@ObjectField(order = 202) public EventType onPush;
@ObjectField(order = 203) public EventType onFocusIn;
@ObjectField(order = 204) public EventType onFocusOut;
@ObjectField(order = 205) public EventType onLeft;
@ObjectField(order = 206) public EventType onRight;
@ObjectField(order = 207) public EventType onUp;
@ObjectField(order = 208) public EventType onDown;
@ObjectField(order = 209) public IntType unknownInt32;
@Override
public Object getImage() {
return image.getImage() == null ? null : image.getImage().getImage();
}
@Override
public void setFocus() {
trigger(onFocusIn);
super.setFocus();
}
@Override
public void focusOut() {
trigger(onFocusOut);
super.focusOut();
}
@Override
public void onPush() {
trigger(onPush);
}
@Override
public void onUp() {
trigger(onUp);
}
@Override
public void onDown() {
trigger(onDown);
}
@Override
public void onLeft() {
trigger(onLeft);
}
@Override
public void onRight() {
trigger(onRight);
}
}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later
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.RefType;
import dev.quoije.umd.rco.type.TextType;
/** An XMB item. */
public class XItemObject extends BaseObject {
@ObjectField(order = 1) public ImageType image;
@ObjectField(order = 2) public TextType text;
@ObjectField(order = 3) public RefType unknownRef4;
}
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.IntType;
/** An XMB item list. */
public class XListObject extends BasePositionObject {
@ObjectField(order = 201) public IntType unknownInt16;
@ObjectField(order = 202) public EventType onCursorMove;
@ObjectField(order = 203) public EventType onScrollIn;
@ObjectField(order = 204) public EventType onScrollOut;
@ObjectField(order = 205) public EventType onPush;
@ObjectField(order = 206) public EventType onContextMenu;
@ObjectField(order = 207) public EventType onFocusLeft;
@ObjectField(order = 208) public EventType onFocusRight;
}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later
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.RefType;
import dev.quoije.umd.rco.type.UnknownType;
/** An XMB list. */
public class XMListObject extends BaseObject {
@ObjectField(order = 1) public UnknownType unknown0;
@ObjectField(order = 2) public ImageType image;
@ObjectField(order = 3) public RefType unknownRef3;
}
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.object;
import dev.quoije.umd.rco.ObjectField;
import dev.quoije.umd.rco.type.EventType;
import dev.quoije.umd.rco.type.IntType;
/** An XMB-style menu (cross media bar). */
public class XMenuObject extends BasePositionObject {
@ObjectField(order = 201) public IntType itemsCount;
@ObjectField(order = 202) public EventType onPush;
@ObjectField(order = 203) public EventType onContextMenu;
@ObjectField(order = 204) public EventType onCursorMove;
@ObjectField(order = 205) public EventType onScrollIn;
@ObjectField(order = 206) public EventType onScrollOut;
}
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.vsmx.objects; package dev.quoije.umd.rco.vsmx.objects;
import dev.quoije.umd.rco.object.BasePositionObject;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxArray; import dev.quoije.umd.rco.vsmx.interpreter.VsmxArray;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject; import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBoolean; import dev.quoije.umd.rco.vsmx.interpreter.VsmxBoolean;
@@ -18,6 +19,7 @@ public class Controller extends BaseNativeObject {
private final MenuHost menuHost; private final MenuHost menuHost;
private VsmxBaseObject userData; private VsmxBaseObject userData;
private String resource; private String resource;
private BasePositionObject focus;
public static VsmxNativeObject create(VsmxInterpreter interpreter, MenuHost menuHost, String resource) { public static VsmxNativeObject create(VsmxInterpreter interpreter, MenuHost menuHost, String resource) {
Controller controller = new Controller(interpreter, menuHost); Controller controller = new Controller(interpreter, menuHost);
@@ -66,18 +68,44 @@ public class Controller extends BaseNativeObject {
return VsmxBoolean.getValue(true); return VsmxBoolean.getValue(true);
} }
public void setFocus(BasePositionObject newFocus) {
if (focus != null) {
focus.focusOut();
}
focus = newFocus;
}
public BasePositionObject getFocus() {
return focus;
}
public void onUp() { public void onUp() {
if (focus != null) {
focus.onUp();
}
} }
public void onDown() { public void onDown() {
if (focus != null) {
focus.onDown();
}
} }
public void onLeft() { public void onLeft() {
if (focus != null) {
focus.onLeft();
}
} }
public void onRight() { public void onRight() {
if (focus != null) {
focus.onRight();
}
} }
public void onPush() { public void onPush() {
if (focus != null) {
focus.onPush();
}
} }
} }
@@ -1,13 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.rco.vsmx.objects; package dev.quoije.umd.rco.vsmx.objects;
import dev.quoije.umd.rco.Display;
import dev.quoije.umd.rco.RcoFile;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxInterpreter; import dev.quoije.umd.rco.vsmx.interpreter.VsmxInterpreter;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxNativeObject; import dev.quoije.umd.rco.vsmx.interpreter.VsmxNativeObject;
import dev.quoije.umd.rco.vsmx.interpreter.VsmxObject; import dev.quoije.umd.rco.vsmx.interpreter.VsmxObject;
/** /**
* The resource native object, exposing the RCO object tables to the script. * The resource native object, exposing the RCO object tables to the script.
* The object tree population is layered in with the renderer.
*/ */
public class Resource extends BaseNativeObject { public class Resource extends BaseNativeObject {
@@ -16,16 +18,64 @@ public class Resource extends BaseNativeObject {
public static final String childrenName = "children"; public static final String childrenName = "children";
public static final String textureName = "texture"; public static final String textureName = "texture";
public static VsmxNativeObject create(VsmxInterpreter interpreter) { public static VsmxNativeObject create(VsmxInterpreter interpreter, Display display,
VsmxNativeObject vsmxController, RcoFile rco) {
Resource resource = new Resource(); Resource resource = new Resource();
VsmxNativeObject object = new VsmxNativeObject(interpreter, resource); VsmxNativeObject object = new VsmxNativeObject(interpreter, resource);
resource.setObject(object); resource.setObject(object);
object.setPropertyValue("pagetable", new VsmxObject(interpreter, null)); Controller controller = (Controller) vsmxController.getObject();
object.setPropertyValue("animtable", new VsmxObject(interpreter, null)); RcoFile.Entry mainTable = rco.mainTable();
object.setPropertyValue("soundtable", new VsmxObject(interpreter, null)); if (mainTable != null) {
object.setPropertyValue("texturetable", new VsmxObject(interpreter, null)); createTable(interpreter, display, controller, object, mainTable, RcoFile.TABLE_OBJ, "pagetable");
createTable(interpreter, display, controller, object, mainTable, RcoFile.TABLE_ANIM, "animtable");
createTable(interpreter, display, controller, object, mainTable, RcoFile.TABLE_SOUND, "soundtable");
createTable(interpreter, display, controller, object, mainTable, RcoFile.TABLE_IMG, "texturetable");
}
return object; return object;
} }
private Resource() {
}
private static RcoFile.Entry[] findEntries(RcoFile.Entry mainTable, int id) {
for (RcoFile.Entry entry : mainTable.subEntries) {
if (entry.id == id) {
return entry.subEntries.toArray(RcoFile.Entry[]::new);
}
}
return null;
}
private static void createObjectFromEntry(VsmxInterpreter interpreter, Display display, Controller controller,
VsmxBaseObject parent, RcoFile.Entry entry) {
if (entry.obj == null) {
return;
}
entry.obj.setDisplay(display);
entry.obj.setController(controller);
VsmxBaseObject object = entry.obj.createVsmxObject(interpreter, parent, entry);
if (!entry.subEntries.isEmpty()) {
VsmxObject children = new VsmxObject(interpreter, null);
object.setPropertyValue(childrenName, children);
for (RcoFile.Entry child : entry.subEntries) {
createObjectFromEntry(interpreter, display, controller, children, child);
}
}
}
private static void createTable(VsmxInterpreter interpreter, Display display, Controller controller,
VsmxObject parent, RcoFile.Entry mainTable, int id, String name) {
VsmxObject table = new VsmxObject(interpreter, null);
parent.setPropertyValue(name, table);
RcoFile.Entry[] entries = findEntries(mainTable, id);
if (entries != null) {
for (RcoFile.Entry entry : entries) {
createObjectFromEntry(interpreter, display, controller, table, entry);
}
}
}
} }