package com.vitco.importer; import com.vitco.importer.dataStatic.CCVxlStatic; import com.vitco.util.file.FileIn; import com.vitco.util.file.RandomAccessFileIn; import java.io.File; import java.io.IOException; /** * Voxel Importer for "Command & Conquer: Red Alert 2" voxel objects * * Taken from VxlReader * https://github.com/OpenRA/OpenRA/blob/bleed/OpenRA.Game/FileFormats/VxlReader.cs */ public class CCVxlImporter extends AbstractImporter { // constructor public CCVxlImporter(File file, String name) throws IOException { super(file, name); } // type of game (they use different color palettes) public enum NormalType { TiberianSun(2), RedAlert2(4); public final int id; NormalType(int id) { this.id = id; } } // voxel layer private final static class VxlLimb { public String name; public float scale; public float[] bounds; public int[] size; public NormalType type; public int voxelCount; } // read the voxel information for a specific layer private void readVoxelData(RandomAccessFileIn s, VxlLimb l) throws IOException { int baseSize = l.size[0] * l.size[1]; int[] colStart = new int[baseSize]; for (int i = 0; i < baseSize; i++) { colStart[i] = s.readInt32(); } s.seek(4 * baseSize, RandomAccessFileIn.CURRENT); int dataStart = (int) s.getFilePointer(); // Count the voxels in this limb l.voxelCount = 0; for (int i = 0; i < baseSize; i++) { // Empty column if (colStart[i] == -1) { continue; } s.seek(dataStart + colStart[i], RandomAccessFileIn.BEGINNING); int z = 0; do { z += s.readUInt8(); int count = s.readUInt8(); z += count; l.voxelCount += count; s.seek(2 * count + 1, RandomAccessFileIn.CURRENT); } while (z < l.size[2]); } // Read the data for (int i = 0; i < baseSize; i++) { // Empty column if (colStart[i] == -1) continue; s.seek(dataStart + colStart[i], RandomAccessFileIn.BEGINNING); int x = i % l.size[0]; int y = i / l.size[0]; int z = 0; do { z += s.readUInt8(); int count = s.readUInt8(); for (int j = 0; j < count; j++) { int color = s.readUInt8(); s.readUInt8(); //int normal = s.readUInt8(); // add a voxel with correct color addVoxel( x, -z, y, l.type == NormalType.TiberianSun ? CCVxlStatic.COLORS_TIBERIAN_DAWN[color] : CCVxlStatic.COLORS_RED_ALERT[color] ); z++; } // Skip duplicate count s.readUInt8(); } while (z < l.size[2]); } } // read file - returns true if file has loaded correctly @Override protected boolean read(FileIn fileIn, RandomAccessFileIn s) throws IOException { // identifier if (!s.readASCII(16).startsWith("Voxel Animation")) { return false; } // read basic information s.readUInt32(); int limbCount = s.readUInt32(); s.readUInt32(); int bodySize = s.readUInt32(); s.seek(770, RandomAccessFileIn.CURRENT); // Read Limb (layer) headers VxlLimb[] limbs = new VxlLimb[limbCount]; for (int i = 0; i < limbCount; i++) { limbs[i] = new VxlLimb(); limbs[i].name = s.readASCII(16).trim(); s.seek(12, RandomAccessFileIn.CURRENT); } // skip to the limb (layer) footers s.seek(802 + 28 * limbCount + bodySize, RandomAccessFileIn.BEGINNING); int[] limbDataOffset = new int[limbCount]; for (int i = 0; i < limbCount; i++) { limbDataOffset[i] = s.readUInt32(); s.seek(8, RandomAccessFileIn.CURRENT); limbs[i].scale = s.readFloat(); s.seek(48, RandomAccessFileIn.CURRENT); limbs[i].bounds = new float[6]; for (int j = 0; j < 6; j++) { limbs[i].bounds[j] = s.readFloat(); } limbs[i].size = new int[] {s.readByte() & 0xFF, s.readByte() & 0xFF, s.readByte() & 0xFF }; limbs[i].type = s.readByte() == 2 ? NormalType.TiberianSun : NormalType.RedAlert2; } // read the voxel data for all layers for (int i = 0; i < limbCount; i++) { // add a new layer addLayer(limbs[i].name); s.seek(802 + 28*limbCount + limbDataOffset[i], RandomAccessFileIn.BEGINNING); readVoxelData(s, limbs[i]); } // success (i.e. no error) return true; } }