diff --git a/src/main/java/com/seibel/lod/core/objects/a7/data/DataFile.java b/src/main/java/com/seibel/lod/core/objects/a7/data/DataFile.java index c3ec36f77..064e7b1be 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/data/DataFile.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/data/DataFile.java @@ -37,6 +37,7 @@ public class DataFile { public final DhSectionPos pos; public byte dataLevel; public DataSourceLoader loader; + public byte loaderVersion; public Class dataType; public LodDataSource loadedData = null; @@ -55,6 +56,7 @@ public class DataFile { this.dataType = loader.clazz; this.dataLevel = loadedData.getDataDetail(); this.loadedData = loadedData; + this.loaderVersion = loader.loaderSupportedVersions[loader.loaderSupportedVersions.length - 1]; // get latest version } DataFile(File path, MappedByteBuffer meta) throws IOException { @@ -82,14 +84,23 @@ public class DataFile { throw new IOException("Invalid file: Data type loader not found: " + dataTypeId + "(v" + loaderVersion + ")"); } this.dataType = loader.clazz; + this.loaderVersion = loaderVersion; } - - LodDataSource load(DHLevel level) throws IOException { - if (loadedData != null) return loadedData; + public FileInputStream getDataContent() throws IOException { FileInputStream fin = new FileInputStream(path); fin.skipNBytes(METADATA_SIZE); - loadedData = loader.loadData(level, pos, fin); - return loadedData; + return fin; + } + + LodDataSource load(DHLevel level) { + if (loadedData != null) return loadedData; + try { + loadedData = loader.loadData(this, level); + return loadedData; + } catch (IOException e) { + //FIXME: Log and review this handling + return null; + } } public boolean verifyPath() { @@ -102,7 +113,9 @@ public class DataFile { DataSourceSaver saver; if (loader instanceof DataSourceSaver) saver = (DataSourceSaver) loader; else if (loader instanceof OldDataSourceLoader) saver = ((OldDataSourceLoader) loader).getNewSaver(); - else throw new IllegalStateException("Data source does not support saving"); + else saver = null; + if (saver == null) return; + byte newDataLevel = loadedData.getDataDetail(); try (FileOutputStream fout = new FileOutputStream(path, false)) { @@ -119,7 +132,7 @@ public class DataFile { // Write detail level, data level, loader version out.writeByte(pos.sectionDetail); out.writeByte(loadedData.getDataDetail()); - out.writeByte(saver.loaderVersion); + out.writeByte(saver.getSaverVersion()); // Write unused out.writeByte((byte) 0); diff --git a/src/main/java/com/seibel/lod/core/objects/a7/data/DataFileHandler.java b/src/main/java/com/seibel/lod/core/objects/a7/data/DataFileHandler.java index d815ad6b6..45d5845e7 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/data/DataFileHandler.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/data/DataFileHandler.java @@ -136,13 +136,7 @@ public class DataFileHandler implements RenderDataProvider { public CompletableFuture createRenderData(RenderDataSourceLoader renderSourceLoader, DhSectionPos pos) { return CompletableFuture.supplyAsync(() -> { List files = renderSourceLoader.selectFiles(pos, level, getFilesInPos(pos)); - List dataSource = files.stream().map(f -> { - try { - return f.load(level); - } catch (IOException e) { - throw new RuntimeException(e); - } - }).collect(Collectors.toList()); + List dataSource = files.stream().map(f -> f.load(level)).filter(Objects::nonNull).collect(Collectors.toList()); return renderSourceLoader.construct(dataSource, pos, level); }); } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/data/DataSourceLoader.java b/src/main/java/com/seibel/lod/core/objects/a7/data/DataSourceLoader.java index e86fb8f39..5438d123d 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/data/DataSourceLoader.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/data/DataSourceLoader.java @@ -6,6 +6,7 @@ import com.seibel.lod.core.objects.a7.pos.DhSectionPos; import java.io.DataOutputStream; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.util.*; @@ -15,12 +16,13 @@ public abstract class DataSourceLoader { public static final HashMap> datatypeIdRegistry = new HashMap<>(); public final long datatypeId; - public final byte loaderVersion; + public final byte[] loaderSupportedVersions; public final Class clazz; - public DataSourceLoader(Class clazz, long datatypeId, byte loaderVersion) { + public DataSourceLoader(Class clazz, long datatypeId, byte[] loaderSupportedVersions) { this.datatypeId = datatypeId; - this.loaderVersion = loaderVersion; + this.loaderSupportedVersions = loaderSupportedVersions; + Arrays.sort(loaderSupportedVersions); // sort to allow fast access this.clazz = clazz; if (datatypeIdRegistry.containsKey(datatypeId) && datatypeIdRegistry.get(datatypeId) != clazz) { @@ -28,15 +30,22 @@ public abstract class DataSourceLoader { + datatypeIdRegistry.get(datatypeId) + " != " + clazz); } Set loaders = loaderRegistry.get(datatypeId); - if (loaders.stream().anyMatch(l -> l.loaderVersion == loaderVersion)) { - throw new IllegalArgumentException("Loader for class " + clazz + " with version " + loaderVersion " already registered!"); + if (loaders.stream().anyMatch(other -> { + // see if any loaderSupportsVersion conflicts with this one + for (byte otherVer : other.loaderSupportedVersions) { + if (Arrays.binarySearch(loaderSupportedVersions, otherVer) >= 0) return true; + } + return false; + })) { + throw new IllegalArgumentException("Loader for class " + clazz + " that supports one of the version in " + + loaderSupportedVersions " already registered!"); } datatypeIdRegistry.put(datatypeId, clazz); loaderRegistry.put(datatypeId, this); } // Can return null as meaning the requirement is not met - public abstract LodDataSource loadData(DHLevel level, DhSectionPos sectionPos, InputStream data); + public abstract LodDataSource loadData(DataFile dataFile, DHLevel level) throws IOException; public List foldersToScan(File levelFolderPath) { return Collections.emptyList(); @@ -44,7 +53,7 @@ public abstract class DataSourceLoader { public static DataSourceLoader getLoader(long dataTypeId, byte loaderVersion) { return loaderRegistry.get(dataTypeId).stream() - .filter(l -> l.loaderVersion == loaderVersion) + .filter(l -> Arrays.binarySearch(l.loaderSupportedVersions, loaderVersion) >= 0) .findFirst().orElse(null); } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataSource.java b/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataSource.java index 5051eba3e..99cfc9efd 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataSource.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataSource.java @@ -7,7 +7,6 @@ import java.util.Objects; public interface LodDataSource { DataSourceLoader getLatestLoader(); - DhSectionPos getSectionPos(); byte getDataDetail(); } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/Alpha6DataLoader.java b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/Alpha6DataLoader.java index 14898a73b..39bbd3da4 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/Alpha6DataLoader.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/Alpha6DataLoader.java @@ -2,36 +2,46 @@ package com.seibel.lod.core.objects.a7.datatype.column; import com.seibel.lod.core.enums.config.VerticalQuality; import com.seibel.lod.core.objects.a7.DHLevel; -import com.seibel.lod.core.objects.a7.data.DataFile; -import com.seibel.lod.core.objects.a7.data.DataFileHandler; -import com.seibel.lod.core.objects.a7.data.LodDataSource; -import com.seibel.lod.core.objects.a7.data.OldFileConverter; +import com.seibel.lod.core.objects.a7.data.*; import com.seibel.lod.core.objects.a7.pos.DhSectionPos; import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; +import java.io.*; import java.util.ArrayList; import java.util.List; public class Alpha6DataLoader extends OldDataSourceLoader implements OldFileConverter { - public Alpha6DataLoader() { - super(ColumnDatatype.class, ColumnDatatype.DATA_TYPE_ID, (byte)0); + public static final Alpha6DataLoader INSTANCE = new Alpha6DataLoader(); + + private Alpha6DataLoader() { + super(OldColumnDatatype.class, OldColumnDatatype.DATA_TYPE_ID, (byte)0); DataFileHandler.CONVERTERS.add(this); } @Override - public LodDataSource loadData(DHLevel level, DhSectionPos sectionPos, InputStream data) { - return null; + public LodDataSource loadData(DataFile dataFile, DHLevel level) { + //TODO: Add decompressor here + try ( + FileInputStream fin = dataFile.getDataContent(); + XZCompressorInputStream xzIn = new XZCompressorInputStream(fin); + DataInputStream dis = new DataInputStream(xzIn); + ) { + return new OldColumnDatatype(dataFile.pos, dis, dataFile.loaderVersion, level, 1); + } catch (IOException e) { + //FIXME: Log error + return null; + } } @Override public DataSourceSaver getNewSaver() { - return null; + return null; // No re-saving of old datatype as any data should be converted to new format before saving } + + + private static DataFile convert(File file, int detailLevel, VerticalQuality quality) { String oldName = file.getName(); String regionStr = oldName.substring("lod.".length(), oldName.length() - ".xz".length()); @@ -46,26 +56,14 @@ public class Alpha6DataLoader extends OldDataSourceLoader implements OldFileConv XZCompressorInputStream inputStream = new XZCompressorInputStream(fileInStream); int fileVersion = inputStream.read(); - datatype = new ColumnDatatype(fileVersion, quality, detailLevel); DhSectionPos pos; - - - File newFilePath = ColumnDataLoader.INSTANCE.generateFilePathAndName(file, - detailLevel, - quality); - - - + //TODO: Implement } catch (Exception e) { e.printStackTrace(); } - int version - - - - + return null; } @Override diff --git a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnDataLoader.java b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnDataLoader.java index d01814463..58d31d1a3 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnDataLoader.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnDataLoader.java @@ -3,33 +3,35 @@ package com.seibel.lod.core.objects.a7.datatype.column; import com.seibel.lod.core.Config; import com.seibel.lod.core.enums.config.VerticalQuality; import com.seibel.lod.core.objects.a7.DHLevel; +import com.seibel.lod.core.objects.a7.data.DataFile; import com.seibel.lod.core.objects.a7.data.DataFileHandler; -import com.seibel.lod.core.objects.a7.data.DataSourceLoader; import com.seibel.lod.core.objects.a7.data.LodDataSource; import com.seibel.lod.core.objects.a7.pos.DhSectionPos; -import java.io.DataOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Inherited; +import java.io.*; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; public class ColumnDataLoader extends DataSourceSaver { - private static final byte COLUMN_DATA_LOADER_VERSION = 10; + private static final byte COLUMN_DATA_LOADER_VERSION = 1; public static final ColumnDataLoader INSTANCE = new ColumnDataLoader(); private ColumnDataLoader() { - super(ColumnDatatype.class, ColumnDatatype.DATA_TYPE_ID, COLUMN_DATA_LOADER_VERSION); + super(ColumnDatatype.class, ColumnDatatype.DATA_TYPE_ID, new byte[]{COLUMN_DATA_LOADER_VERSION}); } @Override - public LodDataSource loadData(DHLevel level, DhSectionPos sectionPos, InputStream data) { - //TODO: Add decompressor here - return ColumnDatatype.loadFile(level, sectionPos, data, COLUMN_DATA_LOADER_VERSION); + public LodDataSource loadData(DataFile dataFile, DHLevel level) { + try ( + FileInputStream fin = dataFile.getDataContent(); + //TODO: Add decompressor here + DataInputStream dis = new DataInputStream(fin); + ) { + return new ColumnDatatype(dataFile.pos, dis, dataFile.loaderVersion, level); + } catch (IOException e) { + //FIXME: Log error + return null; + } } @Override @@ -45,7 +47,7 @@ public class ColumnDataLoader extends DataSourceSaver { public File generateFilePathAndName(File levelFolderPath, DhSectionPos sectionPos, VerticalQuality quality) { return new File(levelFolderPath, "cache" + File.separator + quality.toString() + File.separator + - String.format("%s_v%d-%s%s", clazz.getSimpleName(), loaderVersion, + String.format("%s_v%d-%s%s", clazz.getSimpleName(), COLUMN_DATA_LOADER_VERSION, sectionPos.serialize(), DataFileHandler.FILE_EXTENSION)); } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnDatatype.java b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnDatatype.java index 4ac0d5d4c..92533a655 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnDatatype.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnDatatype.java @@ -4,30 +4,27 @@ import com.seibel.lod.core.objects.LodDataView; import com.seibel.lod.core.objects.a7.DHLevel; import com.seibel.lod.core.objects.a7.LodQuadTree; import com.seibel.lod.core.objects.a7.data.DataSourceLoader; -import com.seibel.lod.core.objects.a7.data.LodDataSource; import com.seibel.lod.core.objects.a7.pos.DhSectionPos; import com.seibel.lod.core.objects.a7.render.RenderDataSource; -import com.seibel.lod.core.objects.a7.render.RenderDataSourceLoader; import com.seibel.lod.core.objects.opengl.RenderBuffer; import com.seibel.lod.core.util.LodUtil; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.atomic.AtomicReference; -public class ColumnDatatype implements LodDataSource, RenderDataSource { +public class ColumnDatatype implements RenderDataSource, IColumnDatatype { public static final boolean DO_SAFETY_CHECKS = true; public static final byte SECTION_SIZE_OFFSET = 6; public static final int SECTION_SIZE = 1 << SECTION_SIZE_OFFSET; - public static final int LATEST_VERSION = 9; - + public static final int LATEST_VERSION = 10; public static final long DATA_TYPE_ID = "ColumnDatatype".hashCode(); - public final int AIR_LODS_SIZE = 16; - public final int AIR_SECTION_SIZE = SECTION_SIZE/AIR_LODS_SIZE; + public static final int AIR_LODS_SIZE = 16; + public static final int AIR_SECTION_SIZE = SECTION_SIZE/AIR_LODS_SIZE; + public final int verticalSize; public final DhSectionPos sectionPos; public final int yOffset; @@ -47,74 +44,43 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource { this.yOffset = yOffset; } + private long[] loadData(DataInputStream inputData, int version, int verticalSize) throws IOException { + switch (version) { + case 1: + return readDataV1(inputData, verticalSize); + default: + throw new IOException("Invalid Data: The version of the data is not supported"); + } + } + private long[] readDataV1(DataInputStream inputData, int tempMaxVerticalData) throws IOException { + int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData; + byte[] data = new byte[x * Long.BYTES]; + short tempMinHeight = Short.reverseBytes(inputData.readShort()); + ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + inputData.readFully(data); + long[] result = new long[x]; + bb.asLongBuffer().get(result); + if (tempMinHeight != yOffset) { + for (int i=0; i= 0) System.arraycopy(data, 0, dataContainer, index + 0, verticalSize); - } - - /** - * This methods will add the data in the given position if certain condition are satisfied - * @param data - * @param posX - * @param posZ - * @param override if override is true we can override data created with same generation mode - * @return - */ + @Override public boolean copyVerticalData(LodDataView data, int posX, int posZ, boolean override) { if (DO_SAFETY_CHECKS) { if (data.size() != verticalSize) @@ -176,16 +117,13 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource { return true; } + @Override public long getData(int posX, int posZ, int verticalIndex) { return dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex]; } - public long getSingleData(int posX, int posZ) - { - return dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize]; - } - + @Override public long[] getAllData(int posX, int posZ) { long[] result = new long[verticalSize]; @@ -194,94 +132,35 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource { return result; } + @Override public ColumnArrayView getVerticalDataView(int posX, int posZ) { return new ColumnArrayView(dataContainer, verticalSize, posX * SECTION_SIZE * verticalSize + posZ * verticalSize, verticalSize); } + @Override public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) { return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, quadX, quadZ, quadXSize, quadZSize); } + @Override public ColumnQuadView getFullQuad() { return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, 0, 0, SECTION_SIZE, SECTION_SIZE); } + @Override public int getVerticalSize() { return verticalSize; } + @Override public boolean doesItExist(int posX, int posZ) { return ColumnDataPoint.doesItExist(getSingleData(posX, posZ)); } - private long[] readDataVersion6(DataInputStream inputData, int tempMaxVerticalData) throws IOException { - int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData; - byte[] data = new byte[x * Long.BYTES]; - ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); - inputData.readFully(data); - long[] result = new long[x]; - bb.asLongBuffer().get(result); - patchVersion9Reorder(result); - patchHeightAndDepth(result,-yOffset); - return result; - } - private long[] readDataVersion7(DataInputStream inputData, int tempMaxVerticalData) throws IOException { - int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData; - byte[] data = new byte[x * Long.BYTES]; - ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); - inputData.readFully(data); - long[] result = new long[x]; - bb.asLongBuffer().get(result); - patchVersion9Reorder(result); - patchHeightAndDepth(result, 64 - yOffset); - return result; - } - private long[] readDataVersion8(DataInputStream inputData, int tempMaxVerticalData) throws IOException { - int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData; - byte[] data = new byte[x * Long.BYTES]; - short tempMinHeight = Short.reverseBytes(inputData.readShort()); - ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); - inputData.readFully(data); - long[] result = new long[x]; - bb.asLongBuffer().get(result); - patchVersion9Reorder(result); - if (tempMinHeight != yOffset) { - patchHeightAndDepth(result,tempMinHeight - yOffset); - } - return result; - } - private long[] readDataVersion9(DataInputStream inputData, int tempMaxVerticalData) throws IOException { - int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData; - byte[] data = new byte[x * Long.BYTES]; - short tempMinHeight = Short.reverseBytes(inputData.readShort()); - ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); - inputData.readFully(data); - long[] result = new long[x]; - bb.asLongBuffer().get(result); - if (tempMinHeight != yOffset) { - patchHeightAndDepth(result,tempMinHeight - yOffset); - } - return result; - } - private static void patchHeightAndDepth(long[] data, int offset) { - for (int i=0; i tLocalVerticalUpdateArrays = ThreadLocal.withInitial(() -> - { - return new long[LodUtil.DETAIL_OPTIONS - 1][]; - }); + @Override public void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ) { ColumnQuadView quadView = lowerDataContainer.getDataInQuad(posX*2, posZ*2, 2,2); @@ -335,36 +214,26 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource { return stringBuilder.toString(); } + @Override public int getMaxNumberOfLods() { return SECTION_SIZE * SECTION_SIZE * getVerticalSize(); } + @Override public long getRoughRamUsage() { return (long) dataContainer.length * Long.BYTES; } - - // Called by ColumnDataLoader - static LodDataSource loadFile(DHLevel level, DhSectionPos pos, InputStream is, int version) { - try (DataInputStream dis = new DataInputStream(is)) { - return new ColumnDatatype(pos, dis, version, level); - } catch (IOException e) { - //FIXME: Log error - return null; - } - } - public static final ColumnRenderLoader COLUMN_LAYER_LOADER = new ColumnRenderLoader(); - public static final ColumnDataLoader COLUMN_DATA_LOADER = ColumnDataLoader.INSTANCE; static { LodQuadTree.registerLayerLoader(COLUMN_LAYER_LOADER, (byte) 7); // 7 or above } @Override public DataSourceLoader getLatestLoader() { - return COLUMN_DATA_LOADER; + return ColumnDataLoader.INSTANCE; } @Override @@ -377,6 +246,11 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource { return (byte) (sectionPos.sectionDetail - SECTION_SIZE_OFFSET); } + @Override + public byte getDetailOffset() { + return SECTION_SIZE_OFFSET; + } + @Override public void enableRender() { @@ -396,10 +270,6 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource { public void dispose() { } - @Override - public byte getDetailOffset() { - return SECTION_SIZE_OFFSET; - } @Override public boolean trySwapRenderBuffer(AtomicReference referenceSlot) { diff --git a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnRenderLoader.java b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnRenderLoader.java index de2e658f6..fdd2da6d4 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnRenderLoader.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/ColumnRenderLoader.java @@ -45,8 +45,8 @@ class ColumnRenderLoader extends RenderDataSourceLoader { byte targetDataLevel = (byte) (sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET); byte sourceDataLevel = dataSources.get(0).getDataDetail(); LodUtil.assertTrue(targetDataLevel >= sourceDataLevel); - if (dataSources.get(0) instanceof ColumnDatatype) { - ColumnDatatype dataSource = (ColumnDatatype) dataSources.get(0); + if (dataSources.get(0) instanceof IColumnDatatype) { + IColumnDatatype dataSource = (IColumnDatatype) dataSources.get(0); DhSectionPos srcPos = dataSource.getSectionPos(); // Note that in here, the source data level will be always < target section level @@ -77,8 +77,8 @@ class ColumnRenderLoader extends RenderDataSourceLoader { byte sourceDataLevel = dataSource.getDataDetail(); DhSectionPos srcPos = dataSource.getSectionPos(); - if (dataSource instanceof ColumnDatatype) { - ColumnDatatype clDataSource = (ColumnDatatype) dataSource; + if (dataSource instanceof IColumnDatatype) { + IColumnDatatype clDataSource = (IColumnDatatype) dataSource; // Note that targetDataLevel can be > source section level int srcX = srcPos.getCorner().getX().toBlock(); @@ -106,6 +106,10 @@ class ColumnRenderLoader extends RenderDataSourceLoader { return renderDataSource; } + private static boolean IsColumnDatatype(Class clazz) { + return IColumnDatatype.class.isAssignableFrom(clazz); + } + @Override public List selectFiles(DhSectionPos sectionPos, DHLevel level, List[] availableFiles) { byte targetDataLevel = (byte) (sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET); @@ -119,7 +123,8 @@ class ColumnRenderLoader extends RenderDataSourceLoader { if (topValidDataLevel == Byte.MIN_VALUE) { for (DataFile dataFile : availableFiles[detail]) { if (dataFile.dataLevel > targetDataLevel) continue; - if (dataFile.dataType == ColumnDatatype.class || dataFile.dataType == FullDatatype.class) { + if (IsColumnDatatype(dataFile.dataType) || dataFile.dataType == FullDatatype.class + || dataFile.dataType == OldColumnDatatype.class) { topValidDataLevel = LodUtil.max(topValidDataLevel, dataFile.dataLevel); break; } @@ -133,7 +138,7 @@ class ColumnRenderLoader extends RenderDataSourceLoader { for (DataFile dataFile : availableFiles[detail]) { if (dataFile.pos.getWidth().toBlock() == sectionPos.getWidth().toBlock()) { - if (dataFile.dataType == ColumnDatatype.class) { + if (IsColumnDatatype(dataFile.dataType)) { singleCoveringColumnFile = dataFile; break; } else if (dataFile.dataType == FullDatatype.class) { @@ -141,7 +146,7 @@ class ColumnRenderLoader extends RenderDataSourceLoader { // Don't break as there may be a column file later. } } else if (dataFile.pos.getWidth().toBlock() > sectionPos.getWidth().toBlock()) { - if (dataFile.dataType == ColumnDatatype.class && singleCoveringColumnFile == null) + if (IsColumnDatatype(dataFile.dataType) && singleCoveringColumnFile == null) singleCoveringColumnFile = dataFile; else if (dataFile.dataType == FullDatatype.class && singleCoveringFullFile == null) singleCoveringFullFile = dataFile; diff --git a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/DataSourceSaver.java b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/DataSourceSaver.java index 3bd6f71ee..f017b43f0 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/DataSourceSaver.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/DataSourceSaver.java @@ -11,14 +11,19 @@ import java.io.File; import java.io.IOException; public abstract class DataSourceSaver extends DataSourceLoader { - public DataSourceSaver(Class clazz, long datatypeId, byte loaderVersion) { - super(clazz, datatypeId, loaderVersion); + public DataSourceSaver(Class clazz, long datatypeId, byte[] loaderSupportedVersions) { + super(clazz, datatypeId, loaderSupportedVersions); } + public abstract void saveData(DHLevel level, LodDataSource loadedData, DataOutputStream out) throws IOException; // generate the default file path and file name based on various parameters. // Ensure the file extension is '.lod'! public File generateFilePathAndName(File levelFolderPath, DHLevel level, DhSectionPos sectionPos) { - return new File(levelFolderPath, String.format("%s_v%d-%s%s", clazz.getSimpleName(), loaderVersion, + return new File(levelFolderPath, String.format("%s_v%d-%s%s", clazz.getSimpleName(), getSaverVersion(), sectionPos.serialize(), DataFileHandler.FILE_EXTENSION)); } + + public byte getSaverVersion() { + return loaderSupportedVersions[loaderSupportedVersions.length - 1]; + } } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/IColumnDatatype.java b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/IColumnDatatype.java new file mode 100644 index 000000000..9adb4b047 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/IColumnDatatype.java @@ -0,0 +1,37 @@ +package com.seibel.lod.core.objects.a7.datatype.column; + +import com.seibel.lod.core.objects.LodDataView; +import com.seibel.lod.core.objects.a7.data.LodDataSource; + +public interface IColumnDatatype extends LodDataSource { + byte getDetailOffset(); + default int getDataSize() { + return 1 << getDetailOffset(); + } + int getMaxNumberOfLods(); + long getRoughRamUsage(); + + int getVerticalSize(); + boolean doesItExist(int posX, int posZ); + long getData(int posX, int posZ, int verticalIndex); + default long getSingleData(int posX, int posZ) {return getData(posX, posZ, 0);} + long[] getAllData(int posX, int posZ); + ColumnArrayView getVerticalDataView(int posX, int posZ); + ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize); + ColumnQuadView getFullQuad(); + + /** + * This method will clear all data at relative section position + */ + void clear(int posX, int posZ); + /** + * This method will add the data given in input at the relative position and vertical index + */ + boolean addData(long data, int posX, int posZ, int verticalIndex); + /** + * This methods will add the data in the given position if certain condition are satisfied + * @param override if override is true we can override data created with same generation mode + */ + boolean copyVerticalData(LodDataView data, int posX, int posZ, boolean override); + void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ); +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/OldColumnDatatype.java b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/OldColumnDatatype.java new file mode 100644 index 000000000..83496219f --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/datatype/column/OldColumnDatatype.java @@ -0,0 +1,249 @@ +package com.seibel.lod.core.objects.a7.datatype.column; + +import com.seibel.lod.core.objects.LodDataView; +import com.seibel.lod.core.objects.a7.DHLevel; +import com.seibel.lod.core.objects.a7.data.DataSourceLoader; +import com.seibel.lod.core.objects.a7.pos.DhSectionPos; + +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class OldColumnDatatype implements IColumnDatatype { + public static final boolean DO_SAFETY_CHECKS = true; + public static final int LATEST_VERSION = 10; + public static final long DATA_TYPE_ID = "OldColumnDatatype".hashCode(); + + public final byte sectionSizeOffset; + public final int getSectSize() { + return 1 << sectionSizeOffset; + } + + public final int verticalSize; + public final DhSectionPos sectionPos; + public final int yOffset; + public final long[] dataContainer; + + /** + * Constructor of the ColumnDataType + * @param maxVerticalSize the maximum vertical size of the container + */ + public OldColumnDatatype(DhSectionPos sectionPos, int maxVerticalSize, int yOffset, int sectionSizeOffset) { + this.sectionSizeOffset = (byte) sectionSizeOffset; + verticalSize = maxVerticalSize; + dataContainer = new long[getSectSize() * getSectSize() * verticalSize]; + this.sectionPos = sectionPos; + this.yOffset = yOffset; + } + + private long[] loadData(DataInputStream inputData, int version, int verticalSize) throws IOException { + switch (version) { + case 6: + return readDataVersion6(inputData, verticalSize); + case 7: + return readDataVersion7(inputData, verticalSize); + case 8: + return readDataVersion8(inputData, verticalSize); + case 9: + case 10: + return readDataVersion9(inputData, verticalSize); + default: + throw new IOException("Invalid Data: The version of the data is not supported"); + } + } + + // Load from data stream with maxVerticalSize loaded from the data stream + public OldColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, DHLevel level, int sectionSizeOffset) throws IOException { + this.sectionSizeOffset = (byte) sectionSizeOffset; + this.sectionPos = sectionPos; + yOffset = level.getMinY(); + byte detailLevel = inputData.readByte(); + if (sectionPos.sectionDetail - sectionSizeOffset != detailLevel) { + throw new IOException("Invalid data: detail level does not match"); + } + verticalSize = inputData.readByte() & 0b01111111; + dataContainer = loadData(inputData, version, verticalSize); + } + + @Override + public byte getDetailOffset() { + return sectionSizeOffset; + } + + public void clear(int posX, int posZ) + { + throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." + + " Convert to ColumnDatatype first before doing any modifications."); + } + + public boolean addData(long data, int posX, int posZ, int verticalIndex) + { + throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." + + " Convert to ColumnDatatype first before doing any modifications."); + } + + public boolean copyVerticalData(LodDataView data, int posX, int posZ, boolean override) { + throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." + + " Convert to ColumnDatatype first before doing any modifications."); + } + + public long getData(int posX, int posZ, int verticalIndex) + { + return dataContainer[posX * getSectSize() * verticalSize + posZ * verticalSize + verticalIndex]; + } + + public long[] getAllData(int posX, int posZ) + { + long[] result = new long[verticalSize]; + int index = posX * getSectSize() * verticalSize + posZ * verticalSize; + System.arraycopy(dataContainer, index, result, 0, verticalSize); + return result; + } + + public ColumnArrayView getVerticalDataView(int posX, int posZ) { + return new ColumnArrayView(dataContainer, verticalSize, + posX * getSectSize() * verticalSize + posZ * verticalSize, verticalSize); + } + + public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) { + return new ColumnQuadView(dataContainer, getSectSize(), verticalSize, quadX, quadZ, quadXSize, quadZSize); + } + public ColumnQuadView getFullQuad() { + return new ColumnQuadView(dataContainer, getSectSize(), verticalSize, 0, 0, getSectSize(), getSectSize()); + } + + public int getVerticalSize() + { + return verticalSize; + } + + public boolean doesItExist(int posX, int posZ) + { + return ColumnDataPoint.doesItExist(getSingleData(posX, posZ)); + } + + @Override + public void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ) { + throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." + + " Convert to ColumnDatatype first before doing any modifications."); + } + + private long[] readDataVersion6(DataInputStream inputData, int tempMaxVerticalData) throws IOException { + int x = getSectSize() * getSectSize() * tempMaxVerticalData; + byte[] data = new byte[x * Long.BYTES]; + ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + inputData.readFully(data); + long[] result = new long[x]; + bb.asLongBuffer().get(result); + patchVersion9Reorder(result); + patchHeightAndDepth(result,-yOffset); + return result; + } + private long[] readDataVersion7(DataInputStream inputData, int tempMaxVerticalData) throws IOException { + int x = getSectSize() * getSectSize() * tempMaxVerticalData; + byte[] data = new byte[x * Long.BYTES]; + ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + inputData.readFully(data); + long[] result = new long[x]; + bb.asLongBuffer().get(result); + patchVersion9Reorder(result); + patchHeightAndDepth(result, 64 - yOffset); + return result; + } + private long[] readDataVersion8(DataInputStream inputData, int tempMaxVerticalData) throws IOException { + int x = getSectSize() * getSectSize() * tempMaxVerticalData; + byte[] data = new byte[x * Long.BYTES]; + short tempMinHeight = Short.reverseBytes(inputData.readShort()); + ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + inputData.readFully(data); + long[] result = new long[x]; + bb.asLongBuffer().get(result); + patchVersion9Reorder(result); + if (tempMinHeight != yOffset) { + patchHeightAndDepth(result,tempMinHeight - yOffset); + } + return result; + } + private long[] readDataVersion9(DataInputStream inputData, int tempMaxVerticalData) throws IOException { + int x = getSectSize() * getSectSize() * tempMaxVerticalData; + byte[] data = new byte[x * Long.BYTES]; + short tempMinHeight = Short.reverseBytes(inputData.readShort()); + ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + inputData.readFully(data); + long[] result = new long[x]; + bb.asLongBuffer().get(result); + if (tempMinHeight != yOffset) { + patchHeightAndDepth(result,tempMinHeight - yOffset); + } + return result; + } + + private static void patchHeightAndDepth(long[] data, int offset) { + for (int i=0; i