From 00091d014f17e62860a49ccdf52e604327acc7e9 Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Wed, 11 Dec 2024 19:38:06 +0100 Subject: [PATCH] Extract shared logic of type readers --- .../decima/game/hfw/ForbiddenWestTest.java | 6 +- ...TIBinaryReader.java => HFWTypeReader.java} | 61 ++++------- .../hfw/storage/StreamingObjectReader.java | 5 +- .../game/hrzr/DirectStorageReaderTest.java | 15 ++- ...IBinaryReader.java => HRZRTypeReader.java} | 86 +++++---------- .../{ => rtti}/UntilDawnTypeFactory.java | 3 +- .../{ => rtti}/UntilDawnTypeId.java | 2 +- .../UntilDawnTypeReader.java} | 100 ++++++------------ .../game/until_dawn/test/UntilDawnMain.java | 9 +- .../decima/game/until_dawn/rtti/RTTITest.java | 2 - .../decima/rtti/io/AbstractTypeReader.java | 47 ++++++++ .../src/main/java/module-info.java | 1 + 12 files changed, 149 insertions(+), 188 deletions(-) rename modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/rtti/{RTTIBinaryReader.java => HFWTypeReader.java} (81%) rename modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/rtti/{RTTIBinaryReader.java => HRZRTypeReader.java} (81%) rename modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/{ => rtti}/UntilDawnTypeFactory.java (96%) rename modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/{ => rtti}/UntilDawnTypeId.java (85%) rename modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/{test/UntilDawnReader.java => rtti/UntilDawnTypeReader.java} (75%) create mode 100644 modules/decima-rtti/src/main/java/com/shade/decima/rtti/io/AbstractTypeReader.java diff --git a/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/ForbiddenWestTest.java b/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/ForbiddenWestTest.java index b0fa310a6..71bf8740a 100644 --- a/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/ForbiddenWestTest.java +++ b/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/ForbiddenWestTest.java @@ -1,8 +1,8 @@ package com.shade.decima.game.hfw; import com.shade.decima.game.hfw.rtti.HFWTypeFactory; +import com.shade.decima.game.hfw.rtti.HFWTypeReader; import com.shade.decima.game.hfw.rtti.HorizonForbiddenWest; -import com.shade.decima.game.hfw.rtti.RTTIBinaryReader; import com.shade.decima.game.hfw.storage.*; import com.shade.util.NotNull; import com.shade.util.io.BinaryReader; @@ -21,7 +21,7 @@ public static void main(String[] args) throws IOException { StreamingGraphResource graph; try (var reader = BinaryReader.open(resolver.resolve("cache:package/streaming_graph.core"))) { - var object = new RTTIBinaryReader().readObject(reader, factory).object(); + var object = new HFWTypeReader().readObject(reader, factory).object(); graph = new StreamingGraphResource((HorizonForbiddenWest.StreamingGraphResource) object, factory); } @@ -33,7 +33,7 @@ public static void main(String[] args) throws IOException { ObjectStreamingSystem system = new ObjectStreamingSystem(device, graph); StreamingObjectReader reader = new StreamingObjectReader(system, factory); - RTTIBinaryReader.ObjectInfo result = reader.readObject("00377119-c8e7-45d7-b37d-0f6e240c3116"); + HFWTypeReader.ObjectInfo result = reader.readObject("00377119-c8e7-45d7-b37d-0f6e240c3116"); System.out.println(result); } } diff --git a/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/rtti/RTTIBinaryReader.java b/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/rtti/HFWTypeReader.java similarity index 81% rename from modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/rtti/RTTIBinaryReader.java rename to modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/rtti/HFWTypeReader.java index 28035afaa..52a44d41c 100644 --- a/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/rtti/RTTIBinaryReader.java +++ b/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/rtti/HFWTypeReader.java @@ -1,9 +1,9 @@ package com.shade.decima.game.hfw.rtti; import com.shade.decima.game.hfw.rtti.HorizonForbiddenWest.GGUUID; -import com.shade.decima.rtti.data.ExtraBinaryDataHolder; import com.shade.decima.rtti.data.Ref; import com.shade.decima.rtti.factory.TypeFactory; +import com.shade.decima.rtti.io.AbstractTypeReader; import com.shade.decima.rtti.runtime.*; import com.shade.util.NotImplementedException; import com.shade.util.NotNull; @@ -20,7 +20,7 @@ import static com.shade.decima.game.hfw.rtti.HorizonForbiddenWest.RTTIRefObject; -public class RTTIBinaryReader { +public class HFWTypeReader extends AbstractTypeReader { public record ObjectInfo(@NotNull RTTIRefObject object, @NotNull ClassTypeInfo info) {} @NotNull @@ -45,19 +45,9 @@ public ObjectInfo readObject(@NotNull BinaryReader reader, @NotNull TypeFactory } @NotNull - private Object readType(@NotNull TypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { - return switch (info) { - case AtomTypeInfo t -> readAtom(t, reader, factory); - case EnumTypeInfo t -> readEnum(t, reader, factory); - case ClassTypeInfo t -> readCompound(t, reader, factory); - case ContainerTypeInfo t -> readContainer(t, reader, factory); - case PointerTypeInfo t -> readPointer(t, reader, factory); - }; - } - - @NotNull + @Override @SuppressWarnings("DuplicateBranchesInSwitch") - private Object readAtom(@NotNull AtomTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { + protected Object readAtom(@NotNull AtomTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { return switch (info.name().name()) { // Simple types case "bool" -> reader.readByteBoolean(); @@ -100,30 +90,32 @@ private Object readAtom(@NotNull AtomTypeInfo info, @NotNull BinaryReader reader } @NotNull - private Object readEnum(@NotNull EnumTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { + @Override + protected Object readEnum(@NotNull EnumTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { throw new NotImplementedException(); } @NotNull - protected Object readCompound(@NotNull ClassTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { - Object object = info.newInstance(); - for (ClassAttrInfo attr : info.serializableAttrs()) { - attr.set(object, readType(attr.type().get(), reader, factory)); - } - if (object instanceof ExtraBinaryDataHolder holder) { - holder.deserialize(reader, factory); - } - return object; - } - - @NotNull - private Object readContainer(@NotNull ContainerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { + @Override + protected Object readContainer(@NotNull ContainerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { return switch (info.name().name()) { case "HashMap", "HashSet" -> readHashContainer(info, reader, factory); default -> readSimpleContainer(info, reader, factory); }; } + @Nullable + @Override + protected Object readPointer(@NotNull PointerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { + if (!reader.readByteBoolean()) { + return null; + } else if (info.name().name().equals("UUIDRef")) { + return new UUIDRef<>((GGUUID) readCompound(factory.get(GGUUID.class), reader, factory)); + } else { + return new InternalLink<>(); + } + } + @NotNull private Object readSimpleContainer(@NotNull ContainerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { var itemInfo = info.itemType().get(); @@ -143,7 +135,7 @@ private Object readSimpleContainer(@NotNull ContainerTypeInfo info, @NotNull Bin var array = Array.newInstance((Class) itemType, count); for (int i = 0; i < count; i++) { - Array.set(array, i, readType(itemInfo, reader, factory)); + Array.set(array, i, read(itemInfo, reader, factory)); } if (info.type() == List.class) { @@ -158,17 +150,6 @@ private Object readHashContainer(@NotNull ContainerTypeInfo info, @NotNull Binar throw new NotImplementedException(); } - @Nullable - protected Object readPointer(@NotNull PointerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { - if (!reader.readByteBoolean()) { - return null; - } else if (info.name().name().equals("UUIDRef")) { - return new UUIDRef<>((GGUUID) readCompound(factory.get(GGUUID.class), reader, factory)); - } else { - return new InternalLink<>(); - } - } - @NotNull private static String readString(@NotNull BinaryReader reader) throws IOException { var length = reader.readInt(); diff --git a/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/storage/StreamingObjectReader.java b/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/storage/StreamingObjectReader.java index dc22a4a9c..09b74f4d9 100644 --- a/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/storage/StreamingObjectReader.java +++ b/modules/decima-game-horizon-forbidden-west/src/main/java/com/shade/decima/game/hfw/storage/StreamingObjectReader.java @@ -1,7 +1,6 @@ package com.shade.decima.game.hfw.storage; -import com.shade.decima.game.hfw.rtti.HorizonForbiddenWest.*; -import com.shade.decima.game.hfw.rtti.RTTIBinaryReader; +import com.shade.decima.game.hfw.rtti.HFWTypeReader; import com.shade.decima.rtti.factory.TypeFactory; import com.shade.decima.rtti.runtime.ClassTypeInfo; import com.shade.decima.rtti.runtime.PointerTypeInfo; @@ -20,7 +19,7 @@ import static com.shade.decima.game.hfw.rtti.HorizonForbiddenWest.*; -public class StreamingObjectReader extends RTTIBinaryReader { +public class StreamingObjectReader extends HFWTypeReader { private static final Logger log = LoggerFactory.getLogger(StreamingObjectReader.class); private static final boolean DEBUG = true; diff --git a/modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/DirectStorageReaderTest.java b/modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/DirectStorageReaderTest.java index 5f38421bc..f47bd6387 100644 --- a/modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/DirectStorageReaderTest.java +++ b/modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/DirectStorageReaderTest.java @@ -3,7 +3,7 @@ import com.shade.decima.game.Asset; import com.shade.decima.game.AssetId; import com.shade.decima.game.hrzr.rtti.HRZRTypeFactory; -import com.shade.decima.game.hrzr.rtti.RTTIBinaryReader; +import com.shade.decima.game.hrzr.rtti.HRZRTypeReader; import com.shade.decima.game.hrzr.storage.PackFileManager; import com.shade.decima.game.hrzr.storage.PathResolver; import com.shade.decima.rtti.factory.TypeNotFoundException; @@ -29,6 +29,7 @@ public static void main(String[] args) throws IOException { log.info("Loading archives"); try (var manager = new PackFileManager(resolver)) { var factory = new HRZRTypeFactory(); + var reader = new HRZRTypeReader(); var assets = new HashMap(); for (Asset asset : manager.assets()) { @@ -43,13 +44,11 @@ public static void main(String[] args) throws IOException { var id = asset.id(); var data = BinaryReader.wrap(manager.load(id)); - try (RTTIBinaryReader reader = new RTTIBinaryReader(data, factory)) { - try { - List objects = reader.read(); - log.info("[{}/{}] Read {} objects", index, slice.size(), objects.size()); - } catch (TypeNotFoundException e) { - log.error("[{}/{}] Unable to read: {}", index, slice.size(), e.getMessage()); - } + try { + List objects = reader.read(data, factory); + log.info("[{}/{}] Read {} objects", index, slice.size(), objects.size()); + } catch (TypeNotFoundException e) { + log.error("[{}/{}] Unable to read: {}", index, slice.size(), e.getMessage()); } index++; diff --git a/modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/rtti/RTTIBinaryReader.java b/modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/rtti/HRZRTypeReader.java similarity index 81% rename from modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/rtti/RTTIBinaryReader.java rename to modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/rtti/HRZRTypeReader.java index 546542295..e64bc2c7d 100644 --- a/modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/rtti/RTTIBinaryReader.java +++ b/modules/decima-game-horizon-zero-dawn-remastered/src/main/java/com/shade/decima/game/hrzr/rtti/HRZRTypeReader.java @@ -1,17 +1,19 @@ package com.shade.decima.game.hrzr.rtti; -import com.shade.decima.rtti.data.ExtraBinaryDataHolder; import com.shade.decima.rtti.data.Ref; import com.shade.decima.rtti.data.Value; import com.shade.decima.rtti.factory.TypeFactory; -import com.shade.decima.rtti.runtime.*; +import com.shade.decima.rtti.io.AbstractTypeReader; +import com.shade.decima.rtti.runtime.AtomTypeInfo; +import com.shade.decima.rtti.runtime.ContainerTypeInfo; +import com.shade.decima.rtti.runtime.EnumTypeInfo; +import com.shade.decima.rtti.runtime.PointerTypeInfo; import com.shade.util.NotImplementedException; import com.shade.util.NotNull; import com.shade.util.Nullable; import com.shade.util.hash.Hashing; import com.shade.util.io.BinaryReader; -import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Array; import java.nio.charset.StandardCharsets; @@ -24,32 +26,24 @@ import static com.shade.decima.game.hrzr.rtti.HorizonZeroDawnRemastered.*; -public class RTTIBinaryReader implements Closeable { - private final BinaryReader reader; - private final TypeFactory factory; - +public class HRZRTypeReader extends AbstractTypeReader { private final List> pointers = new ArrayList<>(); - public RTTIBinaryReader(@NotNull BinaryReader reader, @NotNull TypeFactory factory) { - this.reader = reader; - this.factory = factory; - } - @NotNull - public List read() throws IOException { + public List read(@NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { List objects = new ArrayList<>(); - readObjects(objects); + readObjects(objects, reader, factory); resolvePointers(objects); return objects; } - private void readObjects(@NotNull List objects) throws IOException { + private void readObjects(@NotNull List objects, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { while (reader.remaining() > 0) { var hash = reader.readLong(); var size = reader.readInt(); var start = reader.position(); - var object = readCompound(factory.get(HRZRTypeId.of(hash))); + var object = readCompound(factory.get(HRZRTypeId.of(hash)), reader, factory); var end = reader.position(); if (end - start != size) { @@ -82,25 +76,10 @@ private void resolvePointers(@NotNull List objects) { pointers.clear(); } - @Override - public void close() throws IOException { - reader.close(); - } - - @Nullable - private Object readType(@NotNull TypeInfo info) throws IOException { - return switch (info) { - case AtomTypeInfo t -> readAtom(t); - case EnumTypeInfo t -> readEnum(t); - case ClassTypeInfo t -> readCompound(t); - case ContainerTypeInfo t -> readContainer(t); - case PointerTypeInfo t -> readPointer(t); - }; - } - @NotNull + @Override @SuppressWarnings("DuplicateBranchesInSwitch") - private Object readAtom(@NotNull AtomTypeInfo info) throws IOException { + protected Object readAtom(@NotNull AtomTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { return switch (info.name().name()) { // Simple types case "bool" -> reader.readByteBoolean(); @@ -133,7 +112,8 @@ private Object readAtom(@NotNull AtomTypeInfo info) throws IOException { } @NotNull - private Object readEnum(@NotNull EnumTypeInfo info) throws IOException { + @Override + protected Object readEnum(@NotNull EnumTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { int value = switch (info.size()) { case Byte.BYTES -> reader.readByte(); case Short.BYTES -> reader.readShort(); @@ -153,28 +133,17 @@ private static T uncheckedCast(Object object) { } @NotNull - private Object readCompound(@NotNull ClassTypeInfo info) throws IOException { - Object object = info.newInstance(); - for (ClassAttrInfo attr : info.serializableAttrs()) { - attr.set(object, readType(attr.type().get())); - } - if (object instanceof ExtraBinaryDataHolder holder) { - holder.deserialize(reader, factory); - } - return object; - } - - @NotNull - private Object readContainer(@NotNull ContainerTypeInfo info) throws IOException { + @Override + protected Object readContainer(@NotNull ContainerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { return switch (info.name().name()) { // TODO: Containers seem to have a special flag denoting whether it's an array or a map - case "HashMap", "HashSet" -> readHashContainer(info); - default -> readSimpleContainer(info); + case "HashMap", "HashSet" -> readHashContainer(info, reader, factory); + default -> readSimpleContainer(info, reader, factory); }; } @NotNull - private Object readSimpleContainer(@NotNull ContainerTypeInfo info) throws IOException { + private Object readSimpleContainer(@NotNull ContainerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { var itemInfo = info.itemType().get(); var itemType = itemInfo.type(); var count = reader.readInt(); @@ -195,7 +164,7 @@ private Object readSimpleContainer(@NotNull ContainerTypeInfo info) throws IOExc // Slow path var array = Array.newInstance((Class) itemType, count); for (int i = 0; i < count; i++) { - Array.set(array, i, readType(itemInfo)); + Array.set(array, i, read(itemInfo, reader, factory)); } if (info.type() == List.class) { @@ -206,7 +175,7 @@ private Object readSimpleContainer(@NotNull ContainerTypeInfo info) throws IOExc } @NotNull - private Object readHashContainer(@NotNull ContainerTypeInfo info) throws IOException { + private Object readHashContainer(@NotNull ContainerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { var itemInfo = info.itemType().get(); var itemType = itemInfo.type(); var count = reader.readInt(); @@ -217,7 +186,7 @@ private Object readHashContainer(@NotNull ContainerTypeInfo info) throws IOExcep // We don't actually need to store or use it - but we'll have to compute it // when serialization support is added int hash = reader.readInt(); - Array.set(array, i, readType(itemInfo)); + Array.set(array, i, read(itemInfo, reader, factory)); } // TODO: Use specialized type (Map, Set, etc.) @@ -225,15 +194,16 @@ private Object readHashContainer(@NotNull ContainerTypeInfo info) throws IOExcep } @Nullable - private Ref readPointer(@NotNull PointerTypeInfo info) throws IOException { + @Override + protected Ref readPointer(@NotNull PointerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { var type = reader.readByte(); var gguuid = factory.get(GGUUID.class); var ref = switch (type) { case 0 -> null; - case 1 -> new InternalLink<>((GGUUID) readCompound(gguuid)); - case 2 -> new ExternalLink<>((GGUUID) readCompound(gguuid), readString(reader)); - case 3 -> new StreamingRef<>((GGUUID) readCompound(gguuid), readString(reader)); - case 5 -> new UUIDRef<>((GGUUID) readCompound(gguuid)); + case 1 -> new InternalLink<>((GGUUID) readCompound(gguuid, reader, factory)); + case 2 -> new ExternalLink<>((GGUUID) readCompound(gguuid, reader, factory), readString(reader)); + case 3 -> new StreamingRef<>((GGUUID) readCompound(gguuid, reader, factory), readString(reader)); + case 5 -> new UUIDRef<>((GGUUID) readCompound(gguuid, reader, factory)); default -> throw new IllegalArgumentException("Unknown pointer type: " + type); }; pointers.add(ref); diff --git a/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/UntilDawnTypeFactory.java b/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeFactory.java similarity index 96% rename from modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/UntilDawnTypeFactory.java rename to modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeFactory.java index 2c60710fd..63dbaadb0 100644 --- a/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/UntilDawnTypeFactory.java +++ b/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeFactory.java @@ -1,6 +1,5 @@ -package com.shade.decima.game.until_dawn; +package com.shade.decima.game.until_dawn.rtti; -import com.shade.decima.game.until_dawn.rtti.UntilDawn; import com.shade.decima.rtti.factory.AbstractTypeFactory; import com.shade.decima.rtti.factory.TypeId; import com.shade.decima.rtti.runtime.TypeInfo; diff --git a/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/UntilDawnTypeId.java b/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeId.java similarity index 85% rename from modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/UntilDawnTypeId.java rename to modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeId.java index 85892dbd2..70e8f91ca 100644 --- a/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/UntilDawnTypeId.java +++ b/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeId.java @@ -1,4 +1,4 @@ -package com.shade.decima.game.until_dawn; +package com.shade.decima.game.until_dawn.rtti; import com.shade.decima.rtti.factory.TypeId; import com.shade.util.NotNull; diff --git a/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/test/UntilDawnReader.java b/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeReader.java similarity index 75% rename from modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/test/UntilDawnReader.java rename to modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeReader.java index 5c0068964..787516351 100644 --- a/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/test/UntilDawnReader.java +++ b/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/rtti/UntilDawnTypeReader.java @@ -1,18 +1,18 @@ -package com.shade.decima.game.until_dawn.test; +package com.shade.decima.game.until_dawn.rtti; -import com.shade.decima.game.until_dawn.UntilDawnTypeId; -import com.shade.decima.game.until_dawn.rtti.UntilDawn; -import com.shade.decima.rtti.data.ExtraBinaryDataHolder; import com.shade.decima.rtti.data.Ref; import com.shade.decima.rtti.data.Value; import com.shade.decima.rtti.factory.TypeFactory; -import com.shade.decima.rtti.runtime.*; +import com.shade.decima.rtti.io.AbstractTypeReader; +import com.shade.decima.rtti.runtime.AtomTypeInfo; +import com.shade.decima.rtti.runtime.ContainerTypeInfo; +import com.shade.decima.rtti.runtime.EnumTypeInfo; +import com.shade.decima.rtti.runtime.PointerTypeInfo; import com.shade.util.NotImplementedException; import com.shade.util.NotNull; import com.shade.util.Nullable; import com.shade.util.io.BinaryReader; -import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Array; import java.nio.charset.StandardCharsets; @@ -21,42 +21,26 @@ import java.util.List; import java.util.Objects; -public class UntilDawnReader implements Closeable { - private final BinaryReader reader; - private final TypeFactory factory; - - private final Header header; - private final RTTITypeInfo[] typeInfo; - private final int[] objectTypes; - private final ObjectHeader[] objectHeaders; - +public class UntilDawnTypeReader extends AbstractTypeReader { private final List> pointers = new ArrayList<>(); - public UntilDawnReader(@NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { - this.reader = reader; - this.header = Header.read(reader); - this.factory = factory; - + @NotNull + public List read(@NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { + var binHeader = Header.read(reader); var typeInfoCount = reader.readInt(); - this.typeInfo = reader.readObjects(typeInfoCount, RTTITypeInfo::read, RTTITypeInfo[]::new); - + var typeInfo = reader.readObjects(typeInfoCount, RTTITypeInfo::read); var objectTypesCount = reader.readInt(); - this.objectTypes = reader.readInts(objectTypesCount); - + var objectTypes = reader.readInts(objectTypesCount); var totalExplicitObjects = reader.readInt(); - this.objectHeaders = reader.readObjects(objectTypesCount, ObjectHeader::read, ObjectHeader[]::new); - } - - @NotNull - public List read() throws IOException { - List objects = new ArrayList<>(header.assetCount); + var objectHeaders = reader.readObjects(objectTypesCount, ObjectHeader::read); + var objects = new ArrayList<>(binHeader.assetCount); for (int i = 0; i < objectTypes.length; i++) { var start = reader.position(); - var info = typeInfo[objectTypes[i]]; - var header = objectHeaders[i]; - var object = readCompound(factory.get(UntilDawnTypeId.of(info.name))); + var info = typeInfo.get(objectTypes[i]); + var header = objectHeaders.get(i); + var object = readCompound(factory.get(UntilDawnTypeId.of(info.name)), reader, factory); var end = reader.position(); if (header.size > 0 && end - start != header.size) { @@ -66,6 +50,12 @@ public List read() throws IOException { objects.add(object); } + resolvePointers(objects); + + return objects; + } + + private void resolvePointers(@NotNull List objects) { for (Ref pointer : pointers) { if (pointer instanceof LocalRef localRef) { localRef.object = objects.get(localRef.index); @@ -73,40 +63,11 @@ public List read() throws IOException { } pointers.clear(); - - return objects; - } - - @Override - public void close() throws IOException { - reader.close(); - } - - @Nullable - private Object readType(@NotNull TypeInfo info) throws IOException { - return switch (info) { - case AtomTypeInfo t -> readAtom(t); - case EnumTypeInfo t -> readEnum(t); - case ClassTypeInfo t -> readCompound(t); - case ContainerTypeInfo t -> readContainer(t); - case PointerTypeInfo t -> readPointer(t); - }; } @NotNull - private Object readCompound(@NotNull ClassTypeInfo info) throws IOException { - Object object = info.newInstance(); - for (ClassAttrInfo attr : info.serializableAttrs()) { - attr.set(object, readType(attr.type().get())); - } - if (object instanceof ExtraBinaryDataHolder holder) { - holder.deserialize(reader, factory); - } - return object; - } - - @NotNull - private Object readContainer(@NotNull ContainerTypeInfo info) throws IOException { + @Override + protected Object readContainer(@NotNull ContainerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { var itemInfo = info.itemType().get(); var itemType = itemInfo.type(); var count = reader.readInt(); @@ -125,7 +86,7 @@ private Object readContainer(@NotNull ContainerTypeInfo info) throws IOException // Slow path var array = Array.newInstance((Class) itemType, count); for (int i = 0; i < count; i++) { - Array.set(array, i, readType(itemInfo)); + Array.set(array, i, read(itemInfo, reader, factory)); } if (info.type() == List.class) { @@ -136,7 +97,8 @@ private Object readContainer(@NotNull ContainerTypeInfo info) throws IOException } @NotNull - private Object readEnum(@NotNull EnumTypeInfo info) throws IOException { + @Override + protected Object readEnum(@NotNull EnumTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { int value = switch (info.size()) { case Byte.BYTES -> reader.readByte(); case Short.BYTES -> reader.readShort(); @@ -156,8 +118,9 @@ private static T uncheckedCast(Object object) { } @Nullable + @Override @SuppressWarnings("DuplicateBranchesInSwitch") - private Object readAtom(@NotNull AtomTypeInfo info) throws IOException { + protected Object readAtom(@NotNull AtomTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { return switch (info.name().name()) { // Base types case "bool" -> reader.readByteBoolean(); @@ -181,7 +144,8 @@ private Object readAtom(@NotNull AtomTypeInfo info) throws IOException { } @Nullable - private Ref readPointer(@NotNull PointerTypeInfo info) throws IOException { + @Override + protected Ref readPointer(@NotNull PointerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { var kind = reader.readByte(); var pointer = switch (kind) { case 0 -> { diff --git a/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/test/UntilDawnMain.java b/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/test/UntilDawnMain.java index cc6fb0a83..6ed772422 100644 --- a/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/test/UntilDawnMain.java +++ b/modules/decima-game-until-dawn/src/main/java/com/shade/decima/game/until_dawn/test/UntilDawnMain.java @@ -1,6 +1,8 @@ package com.shade.decima.game.until_dawn.test; -import com.shade.decima.game.until_dawn.UntilDawnTypeFactory; +import com.shade.decima.game.until_dawn.rtti.UntilDawnTypeFactory; +import com.shade.decima.game.until_dawn.rtti.UntilDawnTypeReader; +import com.shade.util.io.BinaryReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +32,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO return FileVisitResult.CONTINUE; } log.info("Reading {}", file); - try (UntilDawnReader reader = new UntilDawnReader(CompressedBinaryReader.open(file), factory)) { - var objects = reader.read(); + try (BinaryReader data = CompressedBinaryReader.open(file)) { + var reader = new UntilDawnTypeReader(); + var objects = reader.read(data, factory); log.info("Read {} objects", objects.size()); } return FileVisitResult.CONTINUE; diff --git a/modules/decima-game-until-dawn/src/test/java/com/shade/decima/game/until_dawn/rtti/RTTITest.java b/modules/decima-game-until-dawn/src/test/java/com/shade/decima/game/until_dawn/rtti/RTTITest.java index aa2b3c510..933e7b078 100644 --- a/modules/decima-game-until-dawn/src/test/java/com/shade/decima/game/until_dawn/rtti/RTTITest.java +++ b/modules/decima-game-until-dawn/src/test/java/com/shade/decima/game/until_dawn/rtti/RTTITest.java @@ -1,7 +1,5 @@ package com.shade.decima.game.until_dawn.rtti; -import com.shade.decima.game.until_dawn.UntilDawnTypeFactory; -import com.shade.decima.game.until_dawn.UntilDawnTypeId; import com.shade.decima.rtti.factory.TypeFactory; import com.shade.decima.rtti.runtime.ClassAttrInfo; import com.shade.util.NotNull; diff --git a/modules/decima-rtti/src/main/java/com/shade/decima/rtti/io/AbstractTypeReader.java b/modules/decima-rtti/src/main/java/com/shade/decima/rtti/io/AbstractTypeReader.java new file mode 100644 index 000000000..547165b5e --- /dev/null +++ b/modules/decima-rtti/src/main/java/com/shade/decima/rtti/io/AbstractTypeReader.java @@ -0,0 +1,47 @@ +package com.shade.decima.rtti.io; + +import com.shade.decima.rtti.data.ExtraBinaryDataHolder; +import com.shade.decima.rtti.factory.TypeFactory; +import com.shade.decima.rtti.runtime.*; +import com.shade.util.NotNull; +import com.shade.util.Nullable; +import com.shade.util.io.BinaryReader; + +import java.io.IOException; + +public abstract class AbstractTypeReader { + @Nullable + public Object read(@NotNull TypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { + return switch (info) { + case AtomTypeInfo t -> readAtom(t, reader, factory); + case EnumTypeInfo t -> readEnum(t, reader, factory); + case ClassTypeInfo t -> readCompound(t, reader, factory); + case ContainerTypeInfo t -> readContainer(t, reader, factory); + case PointerTypeInfo t -> readPointer(t, reader, factory); + }; + } + + @NotNull + protected Object readCompound(@NotNull ClassTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException { + Object object = info.newInstance(); + for (ClassAttrInfo attr : info.serializableAttrs()) { + attr.set(object, read(attr.type().get(), reader, factory)); + } + if (object instanceof ExtraBinaryDataHolder holder) { + holder.deserialize(reader, factory); + } + return object; + } + + @Nullable + protected abstract Object readAtom(@NotNull AtomTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException; + + @NotNull + protected abstract Object readEnum(@NotNull EnumTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException; + + @NotNull + protected abstract Object readContainer(@NotNull ContainerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException; + + @Nullable + protected abstract Object readPointer(@NotNull PointerTypeInfo info, @NotNull BinaryReader reader, @NotNull TypeFactory factory) throws IOException; +} diff --git a/modules/decima-rtti/src/main/java/module-info.java b/modules/decima-rtti/src/main/java/module-info.java index e307c2806..d80d5366d 100644 --- a/modules/decima-rtti/src/main/java/module-info.java +++ b/modules/decima-rtti/src/main/java/module-info.java @@ -7,4 +7,5 @@ exports com.shade.decima.rtti.factory; exports com.shade.decima.rtti.runtime; exports com.shade.decima.rtti; + exports com.shade.decima.rtti.io; } \ No newline at end of file