Vendor grrr emote locally and use PNG in README
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco;
|
||||
|
||||
/** The rendering surface backing an RCO menu. Implemented by the UI layer. */
|
||||
public interface Display {
|
||||
|
||||
void setFocus(IDisplay display);
|
||||
|
||||
void add(IDisplay display);
|
||||
|
||||
void remove(IDisplay display);
|
||||
|
||||
void repaint();
|
||||
|
||||
void changeResource();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco;
|
||||
|
||||
/** A renderable RCO object (plane, button, text, ...). */
|
||||
public interface IDisplay {
|
||||
|
||||
Object getImage();
|
||||
|
||||
Object getAnimImage();
|
||||
|
||||
int getX();
|
||||
|
||||
int getY();
|
||||
|
||||
int getWidth();
|
||||
|
||||
int getHeight();
|
||||
|
||||
float getAlpha();
|
||||
|
||||
void setFocus();
|
||||
|
||||
void focusOut();
|
||||
|
||||
void onUp();
|
||||
|
||||
void onDown();
|
||||
|
||||
void onLeft();
|
||||
|
||||
void onRight();
|
||||
|
||||
void onPush();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** Orders the typed fields of an RCO object record for serialization. */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ObjectField {
|
||||
int order();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco;
|
||||
|
||||
import dev.quoije.umd.rco.object.BaseObject;
|
||||
import dev.quoije.umd.rco.object.ImageObject;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Mutable read cursor and shared reference tables used when deserializing
|
||||
* RCO object and animation records.
|
||||
*/
|
||||
public final class RcoContext {
|
||||
|
||||
public byte[] buffer;
|
||||
public int offset;
|
||||
public Map<Integer, String> events;
|
||||
public Map<Integer, ImageObject> images;
|
||||
public Map<Integer, BaseObject> objects;
|
||||
|
||||
public RcoContext(byte[] buffer, int offset, Map<Integer, String> events,
|
||||
Map<Integer, ImageObject> images, Map<Integer, BaseObject> objects) {
|
||||
this.buffer = buffer;
|
||||
this.offset = offset;
|
||||
this.events = events;
|
||||
this.images = images;
|
||||
this.objects = objects;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.object;
|
||||
|
||||
import dev.quoije.umd.rco.Display;
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.RcoContext;
|
||||
import dev.quoije.umd.rco.RcoFile;
|
||||
import dev.quoije.umd.rco.type.BaseType;
|
||||
import dev.quoije.umd.rco.type.EventType;
|
||||
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.VsmxString;
|
||||
import dev.quoije.umd.rco.vsmx.objects.BaseNativeObject;
|
||||
import dev.quoije.umd.rco.vsmx.objects.Controller;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Base of RCO object records (pages, planes, buttons, ...). Fields are
|
||||
* declared as {@link BaseType} subclasses and read/measured via reflection,
|
||||
* mirroring JPCSP's object model.
|
||||
*/
|
||||
public abstract class BaseObject extends BaseNativeObject implements IDisplay {
|
||||
|
||||
protected Display display;
|
||||
protected Controller controller;
|
||||
private String name;
|
||||
private BaseNativeObject parent;
|
||||
|
||||
private Field[] getSortedFields() {
|
||||
Field[] fields = getClass().getFields();
|
||||
Arrays.sort(fields, Comparator.comparingInt(f -> {
|
||||
ObjectField annotation = f.getAnnotation(ObjectField.class);
|
||||
return annotation == null ? Integer.MAX_VALUE : annotation.order();
|
||||
}));
|
||||
return fields;
|
||||
}
|
||||
|
||||
/** Deserializes all typed fields from the record data. */
|
||||
public void read(RcoContext context) {
|
||||
for (Field field : getSortedFields()) {
|
||||
if (BaseType.class.isAssignableFrom(field.getType())) {
|
||||
try {
|
||||
BaseType type = (BaseType) field.get(this);
|
||||
if (type == null) {
|
||||
type = (BaseType) field.getType().getDeclaredConstructor().newInstance();
|
||||
field.set(this, type);
|
||||
}
|
||||
type.read(context);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Size in bytes of the serialized typed fields. */
|
||||
public int size() {
|
||||
int size = 0;
|
||||
for (Field field : getSortedFields()) {
|
||||
if (BaseType.class.isAssignableFrom(field.getType())) {
|
||||
try {
|
||||
BaseType type = (BaseType) field.get(this);
|
||||
if (type == null) {
|
||||
type = (BaseType) field.getType().getDeclaredConstructor().newInstance();
|
||||
}
|
||||
size += type.size();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/** Resolves reference fields against the RCO tables. */
|
||||
public void init(RcoContext context) {
|
||||
for (Field field : getSortedFields()) {
|
||||
if (BaseType.class.isAssignableFrom(field.getType())) {
|
||||
try {
|
||||
BaseType type = (BaseType) field.get(this);
|
||||
if (type != null) {
|
||||
type.init(context);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates the VSMX-side object and registers it under the entry label. */
|
||||
public VsmxBaseObject createVsmxObject(VsmxInterpreter interpreter, VsmxBaseObject parent, RcoFile.Entry entry) {
|
||||
VsmxNativeObject object = new VsmxNativeObject(interpreter, this);
|
||||
setObject(object);
|
||||
if (entry.label != null) {
|
||||
name = entry.label;
|
||||
object.setPropertyValue("name", new VsmxString(interpreter, entry.label));
|
||||
parent.setPropertyValue(entry.label, object);
|
||||
}
|
||||
if (entry.parent != null && entry.parent.vsmxObject instanceof VsmxNativeObject parentNative) {
|
||||
setParent(parentNative.getObject());
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
public void setDisplay(Display display) {
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
public void setController(Controller controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public BaseNativeObject getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(BaseNativeObject parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
protected void trigger(EventType event) {
|
||||
if (event.getEvent() != null && controller != null) {
|
||||
controller.getInterpreter().interpretScript(getObject(), event.getEvent());
|
||||
}
|
||||
}
|
||||
|
||||
protected void onDisplayUpdated() {
|
||||
if (display != null) {
|
||||
display.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getImage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAnimImage() {
|
||||
return getImage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAlpha() {
|
||||
return 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocus() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusOut() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUp() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDown() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLeft() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRight() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[name=" + name + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.object;
|
||||
|
||||
/** An image entry from the RCO IMG table (raw bytes until decoded by the UI). */
|
||||
public final class ImageObject {
|
||||
|
||||
private final byte[] data;
|
||||
|
||||
public ImageObject(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Returns the decoded image, or null (decoded lazily by the renderer). */
|
||||
public Object getImage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageObject[" + (data == null ? 0 : data.length) + " bytes]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
import dev.quoije.umd.rco.RcoContext;
|
||||
import dev.quoije.umd.rco.object.BaseObject;
|
||||
import dev.quoije.umd.rco.object.ImageObject;
|
||||
|
||||
/**
|
||||
* An 8-byte reference field: a 16-bit reference type, a 16-bit unknown
|
||||
* value, and a 32-bit reference index.
|
||||
*/
|
||||
public class BaseReferenceType extends BaseType {
|
||||
|
||||
public static final int REFERENCE_TYPE_NONE = 0xFFFF;
|
||||
public static final int REFERENCE_TYPE_EVENT = 0x400;
|
||||
public static final int REFERENCE_TYPE_TEXT = 0x401;
|
||||
public static final int REFERENCE_TYPE_IMAGE = 0x402;
|
||||
public static final int REFERENCE_TYPE_MODEL = 0x403;
|
||||
public static final int REFERENCE_TYPE_FONT = 0x405;
|
||||
public static final int REFERENCE_TYPE_OBJECT = 0x407;
|
||||
public static final int REFERENCE_TYPE_ANIM = 0x408;
|
||||
public static final int REFERENCE_TYPE_POSITION_OBJECT = 0x409;
|
||||
|
||||
public int referenceType;
|
||||
public int unknownShort;
|
||||
protected String event;
|
||||
protected BaseObject object;
|
||||
protected ImageObject image;
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(RcoContext context) {
|
||||
referenceType = read16(context);
|
||||
unknownShort = read16(context);
|
||||
super.read(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RcoContext context) {
|
||||
switch (referenceType) {
|
||||
case REFERENCE_TYPE_NONE -> { }
|
||||
case REFERENCE_TYPE_EVENT -> event = context.events.get(value);
|
||||
case REFERENCE_TYPE_OBJECT, REFERENCE_TYPE_POSITION_OBJECT, REFERENCE_TYPE_ANIM ->
|
||||
object = context.objects.get(value);
|
||||
case REFERENCE_TYPE_IMAGE -> image = context.images.get(value);
|
||||
default -> { }
|
||||
}
|
||||
super.init(context);
|
||||
}
|
||||
|
||||
public String getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public BaseObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public ImageObject getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "referenceType=0x" + Integer.toHexString(referenceType)
|
||||
+ ", short1=0x" + Integer.toHexString(unknownShort)
|
||||
+ ", value=0x" + Integer.toHexString(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
import dev.quoije.umd.rco.RcoContext;
|
||||
|
||||
/** Base of the typed fields inside an RCO object record. */
|
||||
public class BaseType {
|
||||
|
||||
protected int value;
|
||||
|
||||
public int size() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
protected int read8(RcoContext context) {
|
||||
return context.buffer[context.offset++] & 0xFF;
|
||||
}
|
||||
|
||||
protected int read16(RcoContext context) {
|
||||
return read8(context) | (read8(context) << 8);
|
||||
}
|
||||
|
||||
protected int read32(RcoContext context) {
|
||||
return read16(context) | (read16(context) << 16);
|
||||
}
|
||||
|
||||
public void read(RcoContext context) {
|
||||
value = read32(context);
|
||||
}
|
||||
|
||||
public void init(RcoContext context) {
|
||||
}
|
||||
|
||||
public int getIntValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setIntValue(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public float getFloatValue() {
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
public void setFloatValue(float value) {
|
||||
this.value = (int) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "value=0x" + Integer.toHexString(getIntValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** Reference to a script event. */
|
||||
public class EventType extends BaseReferenceType {
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** A 32-bit floating-point field stored as IEEE 754 bits. */
|
||||
public class FloatType extends BaseType {
|
||||
|
||||
@Override
|
||||
public float getFloatValue() {
|
||||
return Float.intBitsToFloat(super.getIntValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFloatValue(float value) {
|
||||
super.setIntValue(Float.floatToRawIntBits(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntValue() {
|
||||
return (int) getFloatValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntValue(int value) {
|
||||
setFloatValue((float) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "value=" + getFloatValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** Reference to an image entry. */
|
||||
public class ImageType extends BaseReferenceType {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** A 32-bit integer field. */
|
||||
public class IntType extends BaseType {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** Reference to a model entry. */
|
||||
public class ModelType extends BaseReferenceType {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** Reference to an object entry. */
|
||||
public class ObjectType extends BaseReferenceType {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** Reference to an arbitrary RCO entry. */
|
||||
public class RefType extends BaseReferenceType {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** Reference to a text entry. */
|
||||
public class TextType extends BaseReferenceType {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.type;
|
||||
|
||||
/** A 32-bit field of unknown semantics. */
|
||||
public class UnknownType extends BaseType {
|
||||
}
|
||||
Reference in New Issue
Block a user