Skip to content

Commit fdcc3dc

Browse files
committed
Convert legacy chunks
- Convert legacy chunks - Fix some PE blocks' ID differ from PC blocks' ID
1 parent d69fc6f commit fdcc3dc

File tree

7 files changed

+548
-18
lines changed

7 files changed

+548
-18
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/bin/
2-
/test/
2+
/test/
3+
.classpath
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
eclipse.preferences.version=1
2+
org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false

Converter.jar

2.35 MB
Binary file not shown.

require/guava-22.0.jar

2.46 MB
Binary file not shown.

src/com/ljyloo/PE2PC/PE2PC.java

Lines changed: 309 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
import java.util.Map.Entry;
1919

2020
public class PE2PC {
21+
private final static int DATALAYER_BITS_LAGACY = 7;
22+
private final static int BLOCKDATA_BYTES_LAGACY = 32768;
23+
private final static int METADATA_BYTES_LAGACY = 16384;
24+
private final static int SKYLIGHTDATA_BYTES_LAGACY = 16384;
25+
private final static int BLOCKLIGHTDATA_BYTES_LAGACY = 16384;
26+
2127
private final static int DATALAYER_BITS = 4;
2228
private final static int BLOCKDATA_BYTES = 4096;
2329
private final static int METADATA_BYTES = 2048;
@@ -60,7 +66,10 @@ public static void main(String[] args) throws IOException {
6066
return;
6167
}
6268

63-
convert(srcFolder, desFolder);
69+
//convert(srcFolder, desFolder);
70+
System.out.println("Searching for legacy chunks, may take a while...");
71+
convertLegacy(srcFolder, desFolder);
72+
convert(srcFolder, desFolder);
6473
}
6574

6675
private static void printUsageAndExit() {
@@ -81,6 +90,202 @@ private static void printUsageAndExit() {
8190
System.exit(1);
8291
}
8392

93+
public static void legacy(File src) throws IOException{
94+
DB db = null;
95+
System.out.println("Reading...");
96+
try{
97+
Options options = new Options();
98+
options.createIfMissing(false);
99+
db = factory.open(src, options);
100+
101+
DBIterator iterator = db.iterator();
102+
103+
for(iterator.seekToFirst(); iterator.hasNext(); iterator.next()){
104+
byte[] key = iterator.peekNext().getKey();
105+
if (key.length >= 9 && key[8] == 48){
106+
System.out.println(byte2s(key, false));
107+
System.out.println(iterator.peekNext().getValue().length);
108+
break;
109+
}
110+
}
111+
}
112+
catch (Exception e) {
113+
e.printStackTrace();
114+
}
115+
finally{
116+
db.close();
117+
}
118+
}
119+
120+
public static void convertLegacy(File src, File des) throws IOException{
121+
DB db = null;
122+
int totalChunk = 0;
123+
try{
124+
Options options = new Options();
125+
options.createIfMissing(true);
126+
db = factory.open(src, options);
127+
128+
DBIterator iterator = db.iterator();
129+
//ArrayList<byte[]> keys = new ArrayList<byte[]>();
130+
HashMap<String, RegionFile> regions = new HashMap<String, RegionFile>();
131+
try{
132+
for(iterator.seekToFirst(); iterator.hasNext(); iterator.next()){
133+
byte[] key = iterator.peekNext().getKey();
134+
if(key.length == 9 && key[8] == 48){
135+
byte[] value = iterator.peekNext().getValue();
136+
int chunkX = byteArrayToInt(new byte[]{key[3], key[2], key[1], key[0]});
137+
int chunkZ = byteArrayToInt(new byte[]{key[7], key[6], key[5], key[4]});
138+
System.out.print("\rConverting legacy chunk X: "+chunkX+" Z: "+chunkZ+" ");
139+
System.out.flush();
140+
totalChunk++;
141+
CompoundTag tag = new CompoundTag();
142+
CompoundTag levelData = new CompoundTag();
143+
tag.put("Level", levelData);
144+
145+
levelData.putByte("LightPopulated", (byte)1);
146+
levelData.putByte("TerrainPopulated", (byte)1);
147+
levelData.putByte("V", (byte)1);
148+
levelData.putInt("xPos", chunkX);
149+
levelData.putInt("zPos", chunkZ);
150+
levelData.putLong("InhabitedTime", 0);
151+
levelData.putLong("LastUpdate", 0);
152+
byte[] biomes = new byte[16 * 16];
153+
for(int i = 0; i <256; i++)
154+
biomes[i] = -1;
155+
levelData.putByteArray("Biomes", biomes);
156+
levelData.put("Entities", new ListTag<CompoundTag>("Entities"));
157+
158+
ListTag<CompoundTag> sectionTags = new ListTag<CompoundTag>("Sections");
159+
160+
LevelDBChunk data = new LevelDBChunk(chunkX, chunkZ);
161+
162+
data.blocks = new byte[BLOCKDATA_BYTES_LAGACY];
163+
System.arraycopy(value, 0, data.blocks, 0, BLOCKDATA_BYTES_LAGACY);
164+
165+
byte[] metadata = new byte[METADATA_BYTES_LAGACY];
166+
System.arraycopy(value, BLOCKDATA_BYTES_LAGACY, metadata, 0, METADATA_BYTES_LAGACY);
167+
data.data = new OldDataLayer(metadata, DATALAYER_BITS_LAGACY);
168+
169+
byte[] skyLightData = new byte[SKYLIGHTDATA_BYTES_LAGACY];
170+
System.arraycopy(value, BLOCKDATA_BYTES_LAGACY + METADATA_BYTES_LAGACY, skyLightData, 0, SKYLIGHTDATA_BYTES_LAGACY);
171+
data.skyLight = new OldDataLayer(skyLightData, DATALAYER_BITS);
172+
173+
byte[] blockLightData = new byte[BLOCKLIGHTDATA_BYTES_LAGACY];
174+
System.arraycopy(value, BLOCKDATA_BYTES_LAGACY + METADATA_BYTES_LAGACY + SKYLIGHTDATA_BYTES_LAGACY, blockLightData, 0, BLOCKLIGHTDATA_BYTES_LAGACY);
175+
data.blockLight = new OldDataLayer(blockLightData, DATALAYER_BITS_LAGACY);
176+
177+
for (int yBase = 0; yBase < (128 / 16); yBase++) {
178+
179+
// find non-air
180+
boolean allAir = true;
181+
for (int x = 0; x < 16 && allAir; x++) {
182+
for (int y = 0; y < 16 && allAir; y++) {
183+
for (int z = 0; z < 16; z++) {
184+
int pos = (x << 11) | (z << 7) | (y + (yBase << 4));
185+
int block = data.blocks[pos];
186+
if (block != 0) {
187+
allAir = false;
188+
break;
189+
}
190+
}
191+
}
192+
}
193+
194+
if (allAir) {
195+
continue;
196+
}
197+
198+
// build section
199+
byte[] blocks = new byte[16 * 16 * 16];
200+
DataLayer dataValues = new DataLayer(blocks.length, 4);
201+
DataLayer skyLight = new DataLayer(blocks.length, 4);
202+
DataLayer blockLight = new DataLayer(blocks.length, 4);
203+
204+
for (int x = 0; x < 16; x++) {
205+
for (int y = 0; y < 16; y++) {
206+
for (int z = 0; z < 16; z++) {
207+
int pos = (x << 11) | (z << 7) | (y + (yBase << 4));
208+
int block = data.blocks[pos];
209+
int extraData = data.data.get(x, y + (yBase << 4), z);
210+
byte[] temp = filter((byte)(block & 0xff), (byte)extraData);
211+
blocks[(y << 8) | (z << 4) | x] = temp[0];
212+
dataValues.set(x, y, z, temp[1]);
213+
skyLight.set(x, y, z, data.skyLight.get(x, y + (yBase << 4), z));
214+
blockLight.set(x, y, z, data.blockLight.get(x, y + (yBase << 4), z));
215+
}
216+
}
217+
}
218+
219+
CompoundTag sectionTag = new CompoundTag();
220+
221+
sectionTag.putByte("Y", (byte) (yBase & 0xff));
222+
sectionTag.putByteArray("Blocks", blocks);
223+
sectionTag.putByteArray("Data", dataValues.data);
224+
sectionTag.putByteArray("SkyLight", skyLight.data);
225+
sectionTag.putByteArray("BlockLight", blockLight.data);
226+
227+
sectionTags.add(sectionTag);
228+
}
229+
levelData.put("Sections", sectionTags);
230+
231+
levelData.put("TileEntities", new ListTag<CompoundTag>("TileEntities"));
232+
int[] heightMap = new int[256];
233+
for(int x = 0; x < 16; x++){
234+
for(int z = 0; z < 16; z++){
235+
for(int y = 127; y >= 0; y--){
236+
int pos = (x << 11) | (z << 7) | y;
237+
int block = data.blocks[pos];
238+
if(block != 0){
239+
heightMap[(x << 4) | z] = y;
240+
break;
241+
}
242+
}
243+
}
244+
}
245+
levelData.putIntArray("HeightMap", heightMap);
246+
247+
String k = (chunkX >> 5) + "." + (chunkZ >> 5);
248+
if(!regions.containsKey(k)){
249+
regions.put(k, new RegionFile(new File(des, "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca")));
250+
}
251+
RegionFile regionDest = regions.get(k);
252+
int regionX = (chunkX % 32 + 32) % 32;
253+
int regionZ = (chunkZ % 32 + 32) % 32;
254+
/*if(chunkX < 0 || chunkZ < 0){
255+
@SuppressWarnings("unused")
256+
int i = 1+1;
257+
}*/
258+
DataOutputStream chunkDataOutputStream = regionDest.getChunkDataOutputStream(regionX, regionZ);
259+
if(chunkDataOutputStream == null){
260+
System.out.println(chunkX % 32);
261+
System.out.println(chunkZ % 32);
262+
}
263+
NbtIo.write(tag, chunkDataOutputStream);
264+
chunkDataOutputStream.close();
265+
}
266+
}
267+
}
268+
finally{
269+
Iterator<Entry<String, RegionFile>> iter = regions.entrySet().iterator();
270+
while (iter.hasNext()){
271+
Entry<String, RegionFile> entry = iter.next();
272+
RegionFile region = entry.getValue();
273+
region.close();
274+
}
275+
iterator.close();
276+
}
277+
}
278+
finally{
279+
db.close();
280+
}
281+
if(totalChunk > 0){
282+
System.out.println("\nDone! Converted legacy chunks: " + totalChunk);
283+
}
284+
else{
285+
System.out.println("\nIt seems that the input data does not contain any legacy chunk.");
286+
}
287+
}
288+
84289
public static void convert(File src, File des) throws IOException{
85290
DB db = null;
86291
int totalChunk = 0;
@@ -182,10 +387,11 @@ public static void convert(File src, File des) throws IOException{
182387
for (int y = 0; y < 16; y++) {
183388
for (int z = 0; z < 16; z++) {
184389
int pos = (x << 8) | (z << 4) | y;
185-
int block = data.blocks[pos];
186-
187-
blocks[(y << 8) | (z << 4) | x] = (byte) (block & 0xff);
188-
dataValues.set(x, y, z, data.data.get(x, y, z));
390+
byte block = data.blocks[pos];
391+
int extraData = data.data.get(x, y, z);
392+
byte[] temp = filter((byte)(block & 0xff), (byte)extraData);
393+
blocks[(y << 8) | (z << 4) | x] = temp[0];
394+
dataValues.set(x, y, z, temp[1]);
189395
//skyLight.set(x, y, z, data.skyLight.get(x, y, z));
190396
//blockLight.set(x, y, z, data.blockLight.get(x, y, z));
191397
skyLight.set(x, y, z, 0xf);
@@ -293,10 +499,106 @@ public static void convert(File src, File des) throws IOException{
293499
db.close();
294500
}
295501
if(totalChunk > 0){
296-
System.out.println("\nDone! totalSubChunks: " + totalChunk);
502+
System.out.println("\nDone! Converted sub chunks: " + totalChunk);
297503
}
298504
else{
299-
System.out.println("Oops! It seems that the input data does not contain any valid chunk.");
505+
System.out.println("\nIt seems that the input data does not contain any sub chunk.");
506+
}
507+
}
508+
509+
/*
510+
* Translate blocks' id which are not shared by PE and PC
511+
* If PE have this id and PC don't, replace with air(id=0)
512+
* I know it's better to store the id replace table in a individual file, but I'm just too lazy to do so...Maybe next time
513+
* */
514+
public static byte[] filter(byte id, byte data){
515+
switch (id) {
516+
case 43:
517+
switch (data) {
518+
case 6: //Nether Brick Slab
519+
return new byte[]{43, 7};
520+
case 7: //Quartz Slab
521+
return new byte[]{43, 6};
522+
default:
523+
return new byte[]{43, data};
524+
}
525+
case 44:
526+
switch (data) {
527+
case 6: //Nether Brick Slab
528+
return new byte[]{44, 7};
529+
case 7: //Quartz Slab
530+
return new byte[]{44, 6};
531+
case 14: //Upper Quartz Slab
532+
return new byte[]{44, 15};
533+
case 15: //Upper Nether Brick Slab
534+
return new byte[]{44, 14};
535+
default:
536+
return new byte[]{44, data};
537+
}
538+
case 85: //fence
539+
switch (data) {
540+
case 0: //oak_fence
541+
return new byte[]{85,0};
542+
case 1: //spruce_fence
543+
return new byte[]{(byte) 188, 0};
544+
case 2: //birch_fence
545+
return new byte[]{(byte) 189, 0};
546+
case 3: //jungle_fence
547+
return new byte[]{(byte) 190, 0};
548+
case 4: //dark_oak_fence
549+
return new byte[]{(byte) 192, 0};
550+
case 5: //acacia_fence
551+
return new byte[]{(byte) 191, 0};
552+
default:
553+
return new byte[]{0, 0};
554+
}
555+
case 95: //invisible bedrock
556+
return new byte[]{(byte) 166, 0}; //barrier
557+
case 125: //dropper
558+
return new byte[]{(byte) 158, data};
559+
case 126: //activator_rail
560+
return new byte[]{(byte) 157, data};
561+
case (byte) 157: //double_wooden_slab
562+
return new byte[]{125, data};
563+
case (byte) 158: //wooden_slab
564+
return new byte[]{126, data};
565+
case (byte) 188: //repeating_command_block
566+
return new byte[]{(byte) 210, data};
567+
case (byte) 189: //chain_command_block
568+
return new byte[]{(byte) 211, data};
569+
case (byte) 198: //grass_path
570+
return new byte[]{(byte) 208, data};
571+
case (byte) 199: //A hanging frame is a block in PE
572+
return new byte[]{0, 0}; //A hanging frame is a entity in PC, so replace it with air
573+
case (byte) 207: //frosted_ice
574+
return new byte[]{(byte) 212, data};
575+
case (byte) 208: //end_rod
576+
return new byte[]{(byte) 198, data};
577+
case (byte) 218: //shulker_box
578+
return new byte[]{(byte) 229, 0};
579+
case (byte) 236: //concrete
580+
return new byte[]{(byte) 251, data};
581+
case (byte) 237: //concrete_powder
582+
return new byte[]{(byte) 252, data};
583+
case (byte) 240: //chorus_plant
584+
return new byte[]{(byte) 199, data};
585+
case (byte) 241: //stained_glass
586+
return new byte[]{(byte) 95, data};
587+
case (byte) 243: //podzol
588+
return new byte[]{(byte) 3, 2};
589+
case (byte) 244: //beetroot
590+
return new byte[]{(byte) 207, data};
591+
case (byte) 245: //stonecutter
592+
case (byte) 246: //glowingobsidian
593+
case (byte) 247: //netherreactor
594+
case (byte) 248: //info_update
595+
case (byte) 249: //info_update2
596+
case (byte) 250: //movingblock
597+
case (byte) 251: //observer
598+
case (byte) 255: //reserved6
599+
return new byte[]{(byte) 0, 0};
600+
default:
601+
return new byte[]{id, data};
300602
}
301603
}
302604

0 commit comments

Comments
 (0)