-
Notifications
You must be signed in to change notification settings - Fork 6
/
RegionToXaero.java
218 lines (185 loc) · 9.17 KB
/
RegionToXaero.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package com.github.entropy5;
import br.com.gamemods.nbtmanipulator.NbtCompound;
import br.com.gamemods.nbtmanipulator.NbtList;
import br.com.gamemods.regionmanipulator.*;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class RegionToXaero {
public static HashSet<Byte> TRANSPARENT = new HashSet<>(Arrays.asList((byte) 0, (byte) 20, (byte) 102, (byte) 95, (byte) 160, (byte) 31, (byte) 31, (byte) 175, (byte) 166, (byte) 132));
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: java -jar RegionToXaero.jar <folder in with mca files> <folder out for xaero zipfiles>");
throw new RuntimeException("Incorrect number of arguments");
}
Path folderIn = new File(args[0]).toPath();
Path folderOut = new File(args[1]).toPath();
System.out.println("Folder in: " + folderIn);
System.out.println("Folder out: " + folderOut);
Arrays.stream(Objects.requireNonNull(folderIn.toFile().listFiles()))
.forEach(fileIn -> {
System.out.println(fileIn.getName());
String[] parts = fileIn.getName().split("\\.");
int rx = Integer.parseInt(parts[1]);
int rz = Integer.parseInt(parts[2]);
String zipName = rx + "_" + rz + ".zip";
File zipFile = folderOut.resolve(zipName).toFile();
try {
saveRegion(fileIn, zipFile);
} catch (Exception e) {
e.printStackTrace();
}
});
}
public static void saveRegion(File fileIn, File zipFile) {
try {
Region region = RegionIO.readRegion(fileIn);
RegionPos rPos = region.getPosition();
DataOutputStream out = null;
try {
final ZipOutputStream zipOut = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zipFile.toPath())));
final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
out = new DataOutputStream(byteOut);
final ZipEntry e = new ZipEntry("region.xaero");
zipOut.putNextEntry(e);
out.write(255); // mimicking logic from Xaero format
out.writeInt(4);
for (int o = 0; o < 8; ++o) {
for (int p = 0; p < 8; ++p) {
out.write(o << 4 | p);
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
int chunkX = 4 * o + i; // 0 to 32
int chunkZ = 4 * p + j; // 0 to 32
ChunkPos cPos = new ChunkPos((rPos.getXPos() << 5) + chunkX, (rPos.getZPos() << 5) + chunkZ);
Chunk chunk = region.get(cPos);
NbtList<NbtCompound> sectionList = null;
if (chunk == null) {
out.writeInt(-1); // Xaero format expects this to skip a chunk
} else {
int[][] chunkBlocks = new int[16][16];
int[][] chunkHeight = new int[16][16];
int[][] chunkLight = new int[16][16];
int[][] chunkBiome = new int[16][16]; // no clue how to pull biome tho
byte[] byteBiomes = chunk.getLevel().getByteArray("Biomes");
sectionList = chunk.getLevel().getCompoundList("Sections");
for (int si = 0; si < sectionList.getSize(); si++) { // upwards through sections
NbtCompound section = sectionList.get(si);
byte[] byteBlocks = section.getByteArray("Blocks"); // 16 x 16 x 16 block ids
byte[] byteLight = section.getByteArray("BlockLight"); // half size array
byte[] byteData = section.getByteArray("Data"); // half size array
int counter = 0;
for (int yy = 0; yy < 16; yy++) { // upwards inside section
for (int zz = 0; zz < 16; zz++) {
for (int xx = 0; xx < 16; xx++) {
byte blockByte = byteBlocks[counter];
if (!TRANSPARENT.contains(blockByte)) { // ignore air on top
int dataByte = counter % 2 == 0 ? (byteData[counter / 2] & 0x0F) : ((byteData[counter / 2] >> 4) & 0x0F);
int lightByte = counter % 2 == 0 ? (byteLight[counter / 2] & 0x0F) : ((byteLight[counter / 2] >> 4) & 0x0F);
int biomeByte = byteBiomes[16 * zz + xx];
chunkBlocks[xx][zz] = (blockByte & 0xFF) + (dataByte << 12);
chunkHeight[xx][zz] = 16 * si + yy;
chunkLight[xx][zz] = lightByte;
chunkBiome[xx][zz] = biomeByte;
}
counter++;
}
}
}
}
for (int x = 0; x < 16; ++x) {
for (int z = 0; z < 16; ++z) {
savePixel(new IronBlock(chunkBlocks[x][z], chunkHeight[x][z], chunkLight[x][z], chunkBiome[x][z]), out); // PLACEHOLDER
}
}
out.write(0); // some version thing
}
}
}
}
}
zipOut.write(byteOut.toByteArray());
zipOut.closeEntry();
zipOut.close();
} finally {
if (out != null) {
out.close();
}
}
} catch (final IOException e) {
e.printStackTrace();
}
}
private static class IronBlock {
private final int colourType; // -1 will destruct whole chunks, 0 good
private final int color;
private final int light;
private final int height;
private final int state;
private final int biome;
public IronBlock(final int blockState, final int height, final int light, final int biome) {
this.state = blockState; // BLOCKSTATE ID
this.height = height;
this.light = light;
this.colourType = 0;
this.color = 0xFF000000; // xaeros custom color
this.biome = biome; // plains
}
public int getParameters() {
final int colourTypeToWrite = this.colourType & 3;
int parameters = (0);
parameters |= this.isNotGrass() ? 1 : 0;
parameters |= this.getNumberOfOverlays() != 0 ? 2 : 0;
parameters |= colourTypeToWrite << 2;
parameters |= this.light << 8;
parameters |= this.height << 12;
parameters |= 1048576;
// ignoring some properties here
return parameters;
}
public boolean isNotGrass() {
return (this.state & -65536) != 0 || (this.state & 4095) != 2;
}
public int getState() {
return state;
}
public int getNumberOfOverlays() {
return 0;
}
public int getColourType() {
return this.colourType; // ig
}
public int getCustomColour() {
return color;
}
public int getBiome() {
return biome;
}
}
private static void savePixel(final IronBlock pixel, final DataOutputStream out) throws IOException {
final int parameters = pixel.getParameters();
out.writeInt(parameters);
if (pixel.isNotGrass()) {
out.writeInt(pixel.getState());
}
int biome;
if (pixel.getColourType() == 3) {
out.writeInt(pixel.getCustomColour());
}
biome = pixel.getBiome();
if (biome != -1) {
if (biome < 255) {
out.write(pixel.getBiome());
} else {
out.write(255);
out.writeInt(biome);
}
}
}
}