Initial commit with the CLI gui baker

This commit is contained in:
Christian Rendina 2024-12-17 13:06:17 +01:00
commit 03ca0c2cf2
42 changed files with 1578 additions and 0 deletions

57
.gitignore vendored Normal file
View File

@ -0,0 +1,57 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
# Project
bin/
temp/
*.pyc
reobf/
logs/
jars/
.idea/
eclipse/**/.settings
eclipse/**/output-*.log
eclipse/**/.idea
*.iml
idea/nativedeps
/idea/dependency-reduced-pom.xml
/stats/
/optionstmcw.txt
/idea/target/
/crash-reports/
/saves/
/target/classes/
/target/
output-server.log.1
.temp/
.directory
Thumbs.db

10
README.MD Normal file
View File

@ -0,0 +1,10 @@
# MineFork GUI baker
GUI creator and editor for MineFork 5.2+
# Usage
To convert from XML to NBT: `java -jar guibaker.jar p (input xml) (output NBT)`
To convert from NBT to XML: `java -jar guibaker.jar u (input NBT) (output XML)`
Example:
`java -jar guibaker.jar p default/mainmenu.xml resources/gui/mainmenu.nbt`

41
guibaker/pom.xml Normal file
View File

@ -0,0 +1,41 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>21</maven.compiler.release>
</properties>
<name>MineFork GUIBaker</name>
<artifactId>guibaker</artifactId>
<version>${project.parent.version}</version>
<parent>
<artifactId>guitools</artifactId>
<groupId>org.scaminc.minefork.guitools</groupId>
<version>0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
<scope>import</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -0,0 +1,59 @@
package org.scaminc.minefork.guitools.baker;
import org.scaminc.minefork.guicore.NBTWriter;
import org.scaminc.minefork.guicore.XMLReader;
import org.scaminc.minefork.guicore.entity.Screen;
import org.scaminc.minefork.guicore.nbt.NBTCompound;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class GuiBaker {
public static void main(String[] args)
{
if (args.length < 3)
{
System.out.println("MineFork GUI Baker");
System.out.println("Usage: p/u [input xml] [output nbt]");
return;
}
if (args[0].equals("p"))
{
String in = args[1];
String out = args[2];
try {
Screen scr = XMLReader.readFile(in);
if (scr == null)
throw new NullPointerException();
NBTCompound comp = NBTWriter.write(scr);
try (FileOutputStream s = new FileOutputStream(out))
{
try (DataOutputStream o = new DataOutputStream(s))
{
comp.write(o);
}
}
System.out.println("NBT successfully written!");
} catch (ParserConfigurationException | IOException | SAXException e) {
throw new RuntimeException(e);
}
}
else if (args[0].equals("u"))
{
System.out.println("Conversion from NBT to XML is not supported yet!");
}
else
{
System.out.println("Unknown arguments");
}
}
}

51
guicore/pom.xml Normal file
View File

@ -0,0 +1,51 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>21</maven.compiler.release>
</properties>
<name>MineFork GUI core</name>
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
<parent>
<artifactId>guitools</artifactId>
<groupId>org.scaminc.minefork.guitools</groupId>
<version>0.1-SNAPSHOT</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.10.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -0,0 +1,133 @@
package org.scaminc.minefork.guicore;
import org.scaminc.minefork.guicore.entity.*;
import org.scaminc.minefork.guicore.nbt.*;
public class NBTWriter {
public static NBTCompound write(Screen scr)
{
NBTOutput out = new NBTOutput();
out.put(NBTTags.IsInGame.value(), scr.IsInGame);
out.put(NBTTags.IsPanorama.value(), scr.IsPanorama);
if (!scr.Children.isEmpty())
{
NBTList lst = new NBTList();
lst.Type = NBTTagTypes.Compound;
lst.Tags = new NBTBase[scr.Children.size()];
for (int i = 0; i < scr.Children.size(); i++)
{
NBTCompound ce = writeElement(scr.Children.get(i));
lst.Tags[i] = ce;
}
out.put(NBTTags.Childs.value(), lst);
}
return out.getResult();
}
private static NBTCompound writeElement(BaseGui baseGui) {
NBTOutput out = new NBTOutput();
out.put(NBTTags.Type.value(), (byte)baseGui.getType().ordinal());
if (baseGui.Id != null)
out.put(NBTTags.Id.value(), baseGui.Id.hashCode());
if (baseGui.Disabled != null)
out.put(NBTTags.Disabled.value(), baseGui.Disabled);
if (baseGui.X != null) {
out.put(NBTTags.PositionX.value(), baseGui.X);
}
if (baseGui.Y != null) {
out.put(NBTTags.PositionY.value(), baseGui.Y);
}
if (baseGui.AnchorX != null)
{
out.put(NBTTags.AnchorX.value(), baseGui.AnchorX.ordinal());
}
if (baseGui.AnchorY != null)
{
out.put(NBTTags.AnchorY.value(), baseGui.AnchorY.ordinal());
}
if (baseGui.ParentId != null)
{
out.put(NBTTags.Parent.value(), baseGui.ParentId.hashCode());
}
if (baseGui instanceof BaseGuiWithSize baseGuisize)
{
if (baseGuisize.Width != null) {
out.put(NBTTags.Width.value(), baseGuisize.Width);
}
if (baseGuisize.Height != null) {
out.put(NBTTags.Height.value(), baseGuisize.Height);
}
}
if (baseGui instanceof Button)
writeButton(out, (Button)baseGui);
else if (baseGui instanceof Label)
writeLabel(out, (Label)baseGui);
else if (baseGui instanceof Panel)
writePanel(out, (Panel)baseGui);
return out.getResult();
}
private static void writePanel(NBTOutput out, Panel baseGui) {
if (baseGui.Color != null)
out.put(NBTTags.Color.value(), baseGui.Color);
}
private static void writeLabel(NBTOutput out, Label baseGui) {
if (baseGui.Scale != null)
out.put(NBTTags.Scale.value(), baseGui.Scale);
if (baseGui.Rotation != null)
out.put(NBTTags.Rotation.value(), baseGui.Rotation);
if (baseGui.ZoomEffect != null)
out.put(NBTTags.Zooming.value(), baseGui.ZoomEffect);
if (baseGui.Color != null)
out.put(NBTTags.Color.value(), baseGui.Color);
if (baseGui.Text != null)
{
out.put(baseGui.IsTextLocalized ? NBTTags.TextLocalized.value() : NBTTags.Text.value(), baseGui.Text);
}
}
private static void writeButton(NBTOutput out, Button baseGui) {
if (baseGui.Text != null)
{
out.put(baseGui.IsTextLocalized ? NBTTags.TextLocalized.value() : NBTTags.Text.value(), baseGui.Text);
}
if (baseGui.Image != null && baseGui.Normal != null)
{
out.put(NBTTags.ImageLocation.value(), baseGui.Image);
out.put(NBTTags.NormalU.value(), baseGui.Normal.u());
out.put(NBTTags.NormalV.value(), baseGui.Normal.v());
if (baseGui.Hover != null)
{
out.put(NBTTags.HoverU.value(), baseGui.Hover.u());
out.put(NBTTags.HoverV.value(), baseGui.Hover.v());
}
if (baseGui.Disabled != null)
{
out.put(NBTTags.DisabledU.value(), baseGui.Disabled.u());
out.put(NBTTags.DisabledV.value(), baseGui.Disabled.v());
}
if (baseGui.DrawRect != null)
{
out.put(NBTTags.DrawBottom.value(), baseGui.DrawRect.bottom());
out.put(NBTTags.DrawTop.value(), baseGui.DrawRect.top());
out.put(NBTTags.DrawLeft.value(), baseGui.DrawRect.left());
out.put(NBTTags.DrawRight.value(), baseGui.DrawRect.right());
}
}
}
}

View File

@ -0,0 +1,274 @@
package org.scaminc.minefork.guicore;
import org.scaminc.minefork.guicore.entity.*;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class XMLReader {
public static Screen readXml(String data) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
ByteArrayInputStream reader = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
Document doc = builder.parse(reader);
return read(doc);
}
public static Screen readFile(String data) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(data);
return read(doc);
}
private static Screen read(Document doc)
{
doc.getDocumentElement().normalize();
NodeList nodes = doc.getElementsByTagName("Screen");
if (nodes.getLength() != 1)
return null;
Node scrXml = nodes.item(0);
Node panorama = scrXml.getAttributes().getNamedItem("panorama");
if (panorama == null)
return null;
Node ingame = scrXml.getAttributes().getNamedItem("ingame");
if (ingame == null)
return null;
Screen scr = new Screen();
scr.IsInGame = Boolean.parseBoolean(ingame.getNodeValue());
scr.IsPanorama = Boolean.parseBoolean(panorama.getNodeValue());
NodeList children = scrXml.getChildNodes();
int childrenlen = children.getLength();
for (int i = 0; i < childrenlen; i++)
{
Node child = children.item(i);
String type = child.getNodeName();
BaseGui res = switch (type) {
case "SplitLogo" -> parseSplitLogo(child);
case "Button" -> parseButton(child);
case "Label" -> parseLabel(child);
case "Image" -> parseImage(child);
case "Panel" -> parsePanel(child);
default -> null;
};
if (res != null)
scr.Children.add(res);
}
return scr;
}
private static boolean getBaseGuiElement(Node child, BaseGui gui)
{
switch (child.getNodeName()) {
case "id" -> {
gui.Id = child.getNodeValue();
return true;
}
case "disabled" -> {
gui.Disabled = Boolean.parseBoolean(child.getNodeValue());
return true;
}
case "x" -> {
gui.X = Integer.parseInt(child.getNodeValue());
return true;
}
case "y" -> {
gui.Y = Integer.parseInt(child.getNodeValue());
return true;
}
case "anchorX" -> {
gui.AnchorX = AnchorX.xmlValueOf(child.getNodeValue());
return true;
}
case "anchorY" -> {
gui.AnchorY = AnchorY.xmlValueOf(child.getNodeValue());
return true;
}
case "parentId" -> {
gui.ParentId = child.getNodeValue();
return true;
}
}
return false;
}
private static boolean getBaseGuiSizeElement(Node child, BaseGuiWithSize gui) {
switch (child.getNodeName())
{
case "width" -> {
gui.Width = Integer.parseInt(child.getNodeValue());
return true;
}
case "height" -> {
gui.Height = Integer.parseInt(child.getNodeValue());
return true;
}
}
return false;
}
private static BaseGui parsePanel(Node child) {
NamedNodeMap attrs = child.getAttributes();
Panel p = new Panel();
for (int i = 0; i < attrs.getLength(); i++)
{
Node attr = attrs.item(i);
if (getBaseGuiElement(attr, p))
continue;
if (getBaseGuiSizeElement(attr, p))
continue;
if (attr.getNodeName().equals("color")) {
p.Color = parseWithHex(attr.getNodeValue());
}
}
return p;
}
private static BaseGui parseImage(Node child) {
NamedNodeMap attrs = child.getAttributes();
Image p = new Image();
for (int i = 0; i < attrs.getLength(); i++)
{
Node attr = attrs.item(i);
getBaseGuiElement(attr, p);
}
return p;
}
private static Integer parseWithHex(String val)
{
if (val.startsWith("0x"))
return Integer.parseInt(val.substring(2), 16);
else if (val.startsWith("0"))
return Integer.parseInt(val.substring(1), 2);
else
return Integer.parseInt(val);
}
private static BaseGui parseLabel(Node child) {
NamedNodeMap attrs = child.getAttributes();
Label p = new Label();
for (int i = 0; i < attrs.getLength(); i++)
{
Node attr = attrs.item(i);
if (getBaseGuiElement(attr, p))
continue;
if (getBaseGuiSizeElement(attr, p))
continue;
switch (attr.getNodeName())
{
case "color" -> p.Color = parseWithHex(attr.getNodeValue());
case "scale" -> p.Scale = Float.parseFloat(attr.getNodeValue());
case "rotation" -> p.Rotation = Float.parseFloat(attr.getNodeValue());
case "zoomEffect" -> p.ZoomEffect = Boolean.parseBoolean(attr.getNodeValue());
case "text" -> {
p.Text = attr.getNodeValue();
p.IsTextLocalized = false;
}
case "localization" -> {
p.Text = attr.getNodeValue();
p.IsTextLocalized = true;
}
}
}
return p;
}
private static BaseGui parseButton(Node child) {
NamedNodeMap attrs = child.getAttributes();
Button p = new Button();
for (int i = 0; i < attrs.getLength(); i++)
{
Node attr = attrs.item(i);
if (getBaseGuiElement(attr, p))
continue;
if (getBaseGuiSizeElement(attr, p))
continue;
switch (attr.getNodeName())
{
case "image" -> p.Image = attr.getNodeValue();
case "normalU" -> p.Normal = new UV(Integer.parseInt(attr.getNodeValue()), p.Normal != null ? p.Normal.v() : 0);
case "normalV" -> p.Normal = new UV(p.Normal != null ? p.Normal.u() : 0, Integer.parseInt(attr.getNodeValue()));
case "hoveredU" -> p.Hover = new UV(Integer.parseInt(attr.getNodeValue()), p.Hover != null ? p.Hover.v() : 0);
case "hoveredV" -> p.Hover = new UV(p.Hover != null ? p.Hover.u() : 0, Integer.parseInt(attr.getNodeValue()));
case "disabledU" -> p.Disabled = new UV(Integer.parseInt(attr.getNodeValue()), p.Disabled != null ? p.Disabled.v() : 0);
case "disabledV" -> p.Disabled = new UV(p.Disabled != null ? p.Disabled.u() : 0, Integer.parseInt(attr.getNodeValue()));
case "text" -> {
p.Text = attr.getNodeValue();
p.IsTextLocalized = false;
}
case "localization" -> {
p.Text = attr.getNodeValue();
p.IsTextLocalized = true;
}
}
}
NodeList children = child.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
{
Node child2 = children.item(i);
if (child2.getNodeName().equals("DrawRect"))
{
attrs = child2.getAttributes();
int t = 0, l = 0, r = 0, b = 0;
for (int j = 0; j < attrs.getLength(); j++)
{
Node node = attrs.item(j);
switch (node.getNodeName())
{
case "left" -> l = Integer.parseInt(node.getNodeValue());
case "right" -> r = Integer.parseInt(node.getNodeValue());
case "top" -> t = Integer.parseInt(node.getNodeValue());
case "bottom" -> b = Integer.parseInt(node.getNodeValue());
}
}
p.DrawRect = new Rect(l, r, t, b);
break;
}
}
return p;
}
private static BaseGui parseSplitLogo(Node child) {
NamedNodeMap attrs = child.getAttributes();
SplitLogo p = new SplitLogo();
for (int i = 0; i < attrs.getLength(); i++)
{
Node attr = attrs.item(i);
getBaseGuiElement(attr, p);
}
return p;
}
}

View File

@ -0,0 +1,26 @@
package org.scaminc.minefork.guicore.entity;
public enum AnchorX {
LEFT_TO_LEFT_OF, LEFT_TO_CENTER_OF, LEFT_TO_RIGHT_OF,
CENTER_TO_LEFT_OF, CENTER_TO_CENTER_OF, CENTER_TO_RIGHT_OF,
RIGHT_TO_LEFT_OF, RIGHT_TO_CENTER_OF, RIGHT_TO_RIGHT_OF;
public static AnchorX xmlValueOf(String name)
{
return switch (name)
{
case "left-to-left" -> LEFT_TO_LEFT_OF;
case "left-to-center" -> LEFT_TO_CENTER_OF;
case "left-to-right" -> LEFT_TO_RIGHT_OF;
case "center-to-left" -> CENTER_TO_LEFT_OF;
case "center-to-center" -> CENTER_TO_CENTER_OF;
case "center-to-right" -> CENTER_TO_RIGHT_OF;
case "right-to-left" -> RIGHT_TO_LEFT_OF;
case "right-to-center" -> RIGHT_TO_CENTER_OF;
case "right-to-right" -> RIGHT_TO_RIGHT_OF;
default -> throw new IllegalStateException("Unexpected value: " + name);
};
}
}

View File

@ -0,0 +1,26 @@
package org.scaminc.minefork.guicore.entity;
public enum AnchorY {
TOP_TO_TOP_OF, TOP_TO_CENTER_OF, TOP_TO_BOTTOM_OF,
CENTER_TO_TOP_OF, CENTER_TO_CENTER_OF, CENTER_TO_BOTTOM_OF,
BOTTOM_TO_TOP_OF, BOTTOM_TO_CENTER_OF, BOTTOM_TO_BOTTOM_OF;
public static AnchorY xmlValueOf(String name)
{
return switch (name)
{
case "top-to-top" -> TOP_TO_TOP_OF;
case "top-to-center" -> TOP_TO_CENTER_OF;
case "top-to-bottom" -> TOP_TO_BOTTOM_OF;
case "center-to-top" -> CENTER_TO_TOP_OF;
case "center-to-center" -> CENTER_TO_CENTER_OF;
case "center-to-bottom" -> CENTER_TO_BOTTOM_OF;
case "bottom-to-top" -> BOTTOM_TO_TOP_OF;
case "bottom-to-center" -> BOTTOM_TO_CENTER_OF;
case "bottom-to-bottom" -> BOTTOM_TO_BOTTOM_OF;
default -> throw new IllegalStateException("Unexpected value: " + name);
};
}
}

View File

@ -0,0 +1,13 @@
package org.scaminc.minefork.guicore.entity;
public abstract class BaseGui {
public String Id = null;
public Integer X = null;
public Integer Y = null;
public AnchorX AnchorX = null;
public AnchorY AnchorY = null;
public String ParentId = null;
public Boolean Disabled = null;
public abstract ObjectTypes getType();
}

View File

@ -0,0 +1,6 @@
package org.scaminc.minefork.guicore.entity;
public abstract class BaseGuiWithSize extends BaseGui {
public Integer Width = null;
public Integer Height = null;
}

View File

@ -0,0 +1,16 @@
package org.scaminc.minefork.guicore.entity;
public class Button extends BaseGuiWithSize {
public String Image = null;
public UV Normal = null;
public UV Hover = null;
public UV Disabled = null;
public Rect DrawRect = null;
public String Text = null;
public boolean IsTextLocalized = false;
@Override
public ObjectTypes getType() {
return ObjectTypes.Button;
}
}

View File

@ -0,0 +1,8 @@
package org.scaminc.minefork.guicore.entity;
public class Image extends BaseGui {
@Override
public ObjectTypes getType() {
return ObjectTypes.Image;
}
}

View File

@ -0,0 +1,15 @@
package org.scaminc.minefork.guicore.entity;
public class Label extends BaseGuiWithSize {
public Float Scale = null;
public Float Rotation = null;
public Boolean ZoomEffect = null;
public Integer Color = null;
public String Text = null;
public boolean IsTextLocalized = false;
@Override
public ObjectTypes getType() {
return ObjectTypes.Label;
}
}

View File

@ -0,0 +1,10 @@
package org.scaminc.minefork.guicore.entity;
public enum ObjectTypes {
Button,
Label,
Panel,
Image,
SplitLogo,
Max;
}

View File

@ -0,0 +1,10 @@
package org.scaminc.minefork.guicore.entity;
public class Panel extends BaseGuiWithSize {
public Integer Color = null;
@Override
public ObjectTypes getType() {
return ObjectTypes.Panel;
}
}

View File

@ -0,0 +1,4 @@
package org.scaminc.minefork.guicore.entity;
public record Rect(int left, int right, int top, int bottom) {
}

View File

@ -0,0 +1,10 @@
package org.scaminc.minefork.guicore.entity;
import java.util.ArrayList;
import java.util.List;
public class Screen {
public boolean IsPanorama = false;
public boolean IsInGame = false;
public List<BaseGui> Children = new ArrayList<>();
}

View File

@ -0,0 +1,8 @@
package org.scaminc.minefork.guicore.entity;
public class SplitLogo extends BaseGui {
@Override
public ObjectTypes getType() {
return ObjectTypes.SplitLogo;
}
}

View File

@ -0,0 +1,4 @@
package org.scaminc.minefork.guicore.entity;
public record UV(int u, int v) {
}

View File

@ -0,0 +1,56 @@
package org.scaminc.minefork.guicore.nbt;
public class NBT {
public static NBTBase newNBT(byte id)
{
if (id < 0 || id >= NBTTagTypes.Max.ordinal())
return null;
NBTTagTypes type = NBTTagTypes.VALUES[id];
return switch (type) {
default -> null;
case End -> new NBTEnd();
case Byte -> new NBTByte();
case Short -> new NBTShort();
case Int -> new NBTInt();
case Long -> new NBTLong();
case Float -> new NBTFloat();
case Double -> new NBTDouble();
case LongArray -> new NBTLongArray();
case IntArray -> new NBTIntArray();
case ByteArray -> new NBTByteArray();
case String -> new NBTString();
case List -> new NBTList();
};
}
public static NBTTagTypes fromNBT(NBTBase d)
{
if (d instanceof NBTList)
return NBTTagTypes.List;
else if (d instanceof NBTShort)
return NBTTagTypes.Short;
else if (d instanceof NBTLong)
return NBTTagTypes.Long;
else if (d instanceof NBTInt)
return NBTTagTypes.Int;
else if (d instanceof NBTIntArray)
return NBTTagTypes.IntArray;
else if (d instanceof NBTLongArray)
return NBTTagTypes.LongArray;
else if (d instanceof NBTByte)
return NBTTagTypes.Byte;
else if (d instanceof NBTByteArray)
return NBTTagTypes.ByteArray;
else if (d instanceof NBTCompound)
return NBTTagTypes.Compound;
else if (d instanceof NBTDouble)
return NBTTagTypes.Double;
else if (d instanceof NBTFloat)
return NBTTagTypes.Float;
else if (d instanceof NBTString)
return NBTTagTypes.String;
return NBTTagTypes.Max;
}
}

View File

@ -0,0 +1,12 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public abstract class NBTBase {
public String Key = "";
public abstract void read(DataInput p) throws IOException;
public abstract void write(DataOutput p) throws IOException;
}

View File

@ -0,0 +1,19 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTByte extends NBTBase {
public byte Data = 0;
@Override
public void read(DataInput p) throws IOException {
Data = p.readByte();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeByte(Data);
}
}

View File

@ -0,0 +1,22 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTByteArray extends NBTBase {
public byte[] Data = null;
@Override
public void read(DataInput p) throws IOException {
int len = p.readInt();
Data = new byte[len];
p.readFully(Data);
}
@Override
public void write(DataOutput p) throws IOException {
p.writeInt(Data.length);
p.write(Data);
}
}

View File

@ -0,0 +1,41 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
public class NBTCompound extends NBTBase {
public ArrayList<NBTBase> Data = null;
@Override
public void read(DataInput p) throws IOException {
while (true)
{
byte id = p.readByte();
NBTBase d = NBT.newNBT(id);
if (id == NBTTagTypes.End.ordinal() || d == null)
break;
d.Key = p.readUTF();
d.read(p);
Data.add(d);
}
}
@Override
public void write(DataOutput p) throws IOException {
for (NBTBase d : Data)
{
NBTTagTypes type = NBT.fromNBT(d);
if (type == NBTTagTypes.Max)
break;
p.writeByte(type.ordinal());
p.writeUTF(d.Key);
d.write(p);
}
p.writeByte(NBTTagTypes.End.ordinal());
}
}

View File

@ -0,0 +1,19 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTDouble extends NBTBase {
public double Data = 0.0;
@Override
public void read(DataInput p) throws IOException {
Data = p.readDouble();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeDouble(Data);
}
}

View File

@ -0,0 +1,16 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
public class NBTEnd extends NBTBase {
@Override
public void read(DataInput p) {
}
@Override
public void write(DataOutput p) {
}
}

View File

@ -0,0 +1,19 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTFloat extends NBTBase {
public float Data = 0.0f;
@Override
public void read(DataInput p) throws IOException {
Data = p.readFloat();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeFloat(Data);
}
}

View File

@ -0,0 +1,19 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTInt extends NBTBase {
public int Data = 0;
@Override
public void read(DataInput p) throws IOException {
Data = p.readInt();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeInt(Data);
}
}

View File

@ -0,0 +1,23 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTIntArray extends NBTBase {
public int[] Data = null;
@Override
public void read(DataInput p) throws IOException {
int len = p.readInt();
Data = new int[len];
for (int i = 0; i < len; i++)
Data[i] = p.readInt();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeInt(Data.length);
for (int datum : Data) p.writeInt(datum);
}
}

View File

@ -0,0 +1,31 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTList extends NBTBase {
public NBTTagTypes Type = NBTTagTypes.Max;
public NBTBase[] Tags = null;
@Override
public void read(DataInput p) throws IOException {
Type = NBTTagTypes.fromByte(p.readByte());
int len = p.readInt();
Tags = new NBTBase[len];
for (int i = 0; i < len; i++)
{
Tags[i] = NBT.newNBT((byte)Type.ordinal());
Tags[i].read(p);
}
}
@Override
public void write(DataOutput p) throws IOException {
p.writeByte(Type.ordinal());
p.writeInt(Tags.length);
for (NBTBase tag : Tags) {
tag.write(p);
}
}
}

View File

@ -0,0 +1,19 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTLong extends NBTBase {
public long Data = 0;
@Override
public void read(DataInput p) throws IOException {
Data = p.readLong();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeLong(Data);
}
}

View File

@ -0,0 +1,23 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTLongArray extends NBTBase {
public long[] Data = null;
@Override
public void read(DataInput p) throws IOException {
int len = p.readInt();
Data = new long[len];
for (int i = 0; i < len; i++)
Data[i] = p.readInt();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeInt(Data.length);
for (long datum : Data) p.writeLong(datum);
}
}

View File

@ -0,0 +1,120 @@
package org.scaminc.minefork.guicore.nbt;
import java.util.ArrayList;
public class NBTOutput {
private final NBTCompound root = new NBTCompound();
public NBTOutput()
{
root.Data = new ArrayList<>();
}
public void put(String key, byte val)
{
NBTByte i = new NBTByte();
i.Key = key;
i.Data = val;
root.Data.add(i);
}
public void put(String key, byte[] val)
{
NBTByteArray i = new NBTByteArray();
i.Key = key;
i.Data = val;
root.Data.add(i);
}
public void put(String key, int val)
{
NBTInt i = new NBTInt();
i.Key = key;
i.Data = val;
root.Data.add(i);
}
public void put(String key, int[] val)
{
NBTIntArray i = new NBTIntArray();
i.Key = key;
i.Data = val;
root.Data.add(i);
}
public void put(String key, long val)
{
NBTLong i = new NBTLong();
i.Key = key;
i.Data = val;
root.Data.add(i);
}
public void put(String key, long[] val)
{
NBTLongArray i = new NBTLongArray();
i.Key = key;
i.Data = val;
root.Data.add(i);
}
public void put(String key, String val)
{
NBTString i = new NBTString();
i.Key = key;
i.Data = val;
root.Data.add(i);
}
public void put(String key, short val)
{
NBTShort s = new NBTShort();
s.Key = key;
s.Data = val;
root.Data.add(s);
}
public void put(String key, boolean val)
{
put(key, val ? (byte)1: (byte)0);
}
public void put(String key, float val)
{
NBTFloat s = new NBTFloat();
s.Key = key;
s.Data = val;
root.Data.add(s);
}
public void put(String key, double val)
{
NBTDouble s = new NBTDouble();
s.Key = key;
s.Data = val;
root.Data.add(s);
}
public void put(String key, NBTCompound val)
{
val.Key = key;
root.Data.add(val);
}
public void put(String key, NBTList lst)
{
lst.Key = key;
root.Data.add(lst);
}
public void put(String key, NBTOutput out)
{
out.root.Key = key;
root.Data.add(out.root);
}
public NBTCompound getResult()
{
return root;
}
}

View File

@ -0,0 +1,19 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTShort extends NBTBase {
public short Data = 0;
@Override
public void read(DataInput p) throws IOException {
Data = p.readShort();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeShort(Data);
}
}

View File

@ -0,0 +1,19 @@
package org.scaminc.minefork.guicore.nbt;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class NBTString extends NBTBase {
public String Data = "";
@Override
public void read(DataInput p) throws IOException {
Data = p.readUTF();
}
@Override
public void write(DataOutput p) throws IOException {
p.writeUTF(Data);
}
}

View File

@ -0,0 +1,28 @@
package org.scaminc.minefork.guicore.nbt;
public enum NBTTagTypes {
End,
Byte,
Short,
Int,
Long,
Float,
Double,
ByteArray,
String,
List,
Compound,
IntArray,
LongArray,
Max;
public static NBTTagTypes[] VALUES = NBTTagTypes.values();
public static NBTTagTypes fromByte(byte p)
{
if (p >= NBTTagTypes.Max.ordinal() || p < 0)
return NBTTagTypes.Max;
return VALUES[p];
}
}

View File

@ -0,0 +1,44 @@
package org.scaminc.minefork.guicore.nbt;
// keep it in sync with minefork
public enum NBTTags {
// Generic tags
Type,
Id,
Disabled,
PositionX,
PositionY,
Width,
Height,
Parent,
AnchorX,
AnchorY,
// Screen tags
IsInGame,
IsPanorama,
Childs,
// Label tags
Rotation,
Scale,
Color,
Text,
TextLocalized,
Zooming,
// Button tags
ImageLocation,
NormalU,
NormalV,
HoverU,
HoverV,
DisabledU,
DisabledV,
DrawLeft,
DrawRight,
DrawTop,
DrawBottom;
public String value()
{
return String.valueOf(ordinal());
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
MineFork GUI schema VERSION 1 (16-12-2024)
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="https://minefork.scaminc.org/gui/XMLSchema" xmlns="https://minefork.scaminc.org/gui/XMLSchema" elementFormDefault="qualified" version="1">
<xs:simpleType name="anchorX">
<xs:restriction base="xs:string">
<xs:enumeration value="left-to-left" />
<xs:enumeration value="left-to-center" />
<xs:enumeration value="left-to-right" />
<xs:enumeration value="center-to-left" />
<xs:enumeration value="center-to-center" />
<xs:enumeration value="center-to-right" />
<xs:enumeration value="right-to-left" />
<xs:enumeration value="right-to-center" />
<xs:enumeration value="right-to-right" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="anchorY">
<xs:restriction base="xs:string">
<xs:enumeration value="top-to-top" />
<xs:enumeration value="top-to-center" />
<xs:enumeration value="top-to-bottom" />
<xs:enumeration value="center-to-top" />
<xs:enumeration value="center-to-center" />
<xs:enumeration value="center-to-bottom" />
<xs:enumeration value="bottom-to-top" />
<xs:enumeration value="bottom-to-center" />
<xs:enumeration value="bottom-to-bottom" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="rect">
<xs:attribute name="left" type="xs:integer" />
<xs:attribute name="right" type="xs:integer" />
<xs:attribute name="top" type="xs:integer" />
<xs:attribute name="bottom" type="xs:integer" />
</xs:complexType>
<xs:complexType name="base">
<xs:attribute name="id" type="xs:string"/>
<xs:attribute name="x" type="xs:integer" />
<xs:attribute name="y" type="xs:integer" />
<xs:attribute name="anchorX" type="anchorX" />
<xs:attribute name="anchorY" type="anchorY" />
<xs:attribute name="parentId" type="xs:string" />
<xs:attribute name="disabled" type="xs:boolean" />
</xs:complexType>
<xs:complexType name="baseWithSize">
<xs:complexContent>
<xs:extension base="base">
<xs:attribute name="width" type="xs:integer" />
<xs:attribute name="height" type="xs:integer" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="Screen">
<xs:annotation>
<xs:documentation>MineFork GUI schema</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="SplitLogo" maxOccurs="unbounded">
<xs:complexType>
<xs:complexContent>
<xs:extension base="base" />
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="Button" maxOccurs="unbounded">
<xs:complexType>
<xs:complexContent>
<xs:extension base="baseWithSize">
<xs:sequence>
<xs:element name="DrawRect" type="rect" />
</xs:sequence>
<xs:attribute name="image" type="xs:string" use="required" />
<xs:attribute name="normalU" type="xs:integer" use="required" />
<xs:attribute name="normalV" type="xs:integer" use="required" />
<xs:attribute name="hoveredU" type="xs:integer" />
<xs:attribute name="hoveredV" type="xs:integer" />
<xs:attribute name="disabledU" type="xs:integer" />
<xs:attribute name="disabledV" type="xs:integer" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="Label" maxOccurs="unbounded">
<xs:complexType>
<xs:complexContent>
<xs:extension base="baseWithSize">
<xs:attribute name="scale" type="xs:float" />
<xs:attribute name="rotation" type="xs:float" />
<xs:attribute name="zoomEffect" type="xs:boolean" />
<xs:attribute name="color" type="xs:integer" />
<xs:attribute name="text" type="xs:string" />
<xs:attribute name="localization" type="xs:string" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="Panel" maxOccurs="unbounded">
<xs:complexType>
<xs:complexContent>
<xs:extension base="baseWithSize">
<xs:attribute name="color" type="xs:integer" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="Image" maxOccurs="unbounded">
<xs:complexType>
<xs:complexContent>
<xs:extension base="base">
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="panorama" type="xs:boolean" default="false" />
<xs:attribute name="ingame" type="xs:boolean" default="false" />
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,63 @@
package org.scaminc.minefork.guicore;
import org.junit.jupiter.api.Test;
import org.scaminc.minefork.guicore.entity.AnchorX;
import org.scaminc.minefork.guicore.entity.AnchorY;
import org.scaminc.minefork.guicore.entity.Button;
import org.scaminc.minefork.guicore.entity.Screen;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.*;
public class TestXmlReader {
private static final String EMPTY_SCREEN = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Screen panorama=\"false\" ingame=\"true\" xmlns=\"https://minefork.scaminc.org/gui/XMLSchema\"/>";
private static final String SCREEN_WITH_TWO_BUTTONS =
"""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Screen panorama="false" ingame="false" xmlns="https://minefork.scaminc.org/gui/XMLSchema">
<Button x="10" y="20" id="ciao" />
<Button x="40" y="40" id="ciao2" parentId="ciao" anchorX="left-to-right" anchorY="bottom-to-center" />
</Screen>""";
/*@Test
public void testSingleXmlWriter() {
Screen s = new Screen();
s.setIngame(true);
s.setPanorama(false);
String p = XMLReader.writeXml(s);
assertEquals(p, EMPTY_SCREEN);
}*/
@Test
public void testSingleXmlReader() throws ParserConfigurationException, IOException, SAXException {
Screen s = XMLReader.readXml(EMPTY_SCREEN);
assertNotNull(s);
assertFalse(s.IsPanorama);
assertTrue(s.IsInGame);
assertTrue(s.Children.isEmpty());
}
@Test
public void testTwoButtonReader() throws ParserConfigurationException, IOException, SAXException {
Screen s = XMLReader.readXml(SCREEN_WITH_TWO_BUTTONS);
assertNotNull(s);
assertFalse(s.IsPanorama);
assertFalse(s.IsInGame);
assertEquals(s.Children.size(), 2);
Button b = (Button) s.Children.getFirst();
assertEquals(b.Position.x(), 10);
assertEquals(b.Position.y(), 20);
assertEquals(b.Id, "ciao");
b = (Button) s.Children.getLast();
assertEquals(b.Position.x(), 40);
assertEquals(b.Position.y(), 40);
assertEquals(b.ParentId, "ciao");
assertEquals(b.Id, "ciao2");
assertEquals(b.AnchorX, AnchorX.LEFT_TO_RIGHT_OF);
assertEquals(b.AnchorY, AnchorY.BOTTOM_TO_CENTER_OF);
}
}

40
guieditor/pom.xml Normal file
View File

@ -0,0 +1,40 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>MineFork GUI Editor</name>
<groupId>org.scaminc.minefork</groupId>
<artifactId>guieditor</artifactId>
<version>0.1-SNAPSHOT</version>
<parent>
<artifactId>guitools</artifactId>
<groupId>org.scaminc.minefork.guitools</groupId>
<version>0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.scaminc.minefork.guitools</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.formdev</groupId>
<artifactId>flatlaf-intellij-themes</artifactId>
<version>3.5.4</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

22
pom.xml Normal file
View File

@ -0,0 +1,22 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>MineFork GUI tools</name>
<groupId>org.scaminc.minefork.guitools</groupId>
<artifactId>guitools</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>21</maven.compiler.release>
</properties>
<modules>
<module>guicore</module>
<module>guibaker</module>
<!--<module>guieditor</module>-->
</modules>
</project>