Skip to content

Commit 82200a7

Browse files
committed
Update to the latest map format
update to PE 1.x map format
1 parent fc9dbd9 commit 82200a7

File tree

3 files changed

+174
-119
lines changed

3 files changed

+174
-119
lines changed

Converter.jar

83.6 KB
Binary file not shown.

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ The converter will experience an error if you have blocks which are not supporte
88

99
## Usage:
1010

11-
There's a video tutorial about this(Made by AntVenom):
12-
https://www.youtube.com/watch?v=EFAkc5L4h48
11+
There's a video tutorial about this(Made by AntVenom): [Link](https://www.youtube.com/watch?v=EFAkc5L4h48)
1312

1413
Import your LevelDB world in and rename it to the name 'world'.
1514
Run start.bat(if you're on Windows) or start.sh(if you're on OS X or Linux) and the converter will now convert it to Anvil format to the output folder of 'worldanvil'.
16-
Find more info at https://forums.pocketmine.net/threads/leveldb-maps-to-anvil.12018/
15+
Find more info at [here](https://forums.pocketmine.net/threads/leveldb-maps-to-anvil.12018/)
1716

18-
If you were familiar with command line interface, using the Converter.jar like this(You need to install java runtime first, which can be downloaded at java.com):
17+
If you were familiar with command line interface, using the Converter.jar like this(You may want to [install java runtime first](java.com)):
18+
19+
```
1920
java -jar Converter.jar <import folder> <export folder>
21+
```
2022

2123
## Misc:
2224

@@ -25,16 +27,14 @@ The mca files generated by the program stores your maps data. They can be found
2527
![](pic/saves_folder.jpg)
2628
![](pic/region_folder.jpg)
2729

28-
Create a superflat new world and copy the mca files into it, open the game and locate your chunks.
30+
Create a superflat new world and copy the mca files into it, open the game and locate your chunks.(Press F3 to see your location)
2931
Don't worry if you were seeing the flat chunks again, the coordinate of your original chunk in the MCPE is exactly the same in here too, you may want to use teleport command to find the chunks.
3032

3133
## Credits:
3234

33-
This program uses leveldb by tinfoiled. See lisense here:
34-
https://github.com/ljyloo/leveldb
35+
This program uses leveldb by tinfoiled. See lisense [here](https://github.com/ljyloo/leveldb)
3536

36-
This program contains code from Mojang: source at:
37-
https://mojang.com/2012/02/new-minecraft-map-format-anvil/
37+
This program contains code from Mojang: source at [here](https://mojang.com/2012/02/new-minecraft-map-format-anvil/)
3838

3939
User-friendly start.bat/start.sh, the folder 'worldanvil' was by @keithkfng.
4040

src/com/ljyloo/PE2PC/PE2PC.java

Lines changed: 165 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import net.minecraft.world.level.chunk.DataLayer;
1010
import net.minecraft.world.level.chunk.OldDataLayer;
1111
import net.minecraft.world.level.chunk.storage.*;
12+
1213
import static org.iq80.leveldb.impl.Iq80DBFactory.*;
1314

1415
import java.io.*;
@@ -17,11 +18,11 @@
1718
import java.util.Map.Entry;
1819

1920
public class PE2PC {
20-
private final static int DATALAYER_BITS = 7;
21-
private final static int BLOCKDATA_BYTES = 32768;
22-
private final static int METADATA_BYTES = 16384;
23-
private final static int SKYLIGHTDATA_BYTES = 16384;
24-
private final static int BLOCKLIGHTDATA_BYTES = 16384;
21+
private final static int DATALAYER_BITS = 4;
22+
private final static int BLOCKDATA_BYTES = 4096;
23+
private final static int METADATA_BYTES = 2048;
24+
private final static int SKYLIGHTDATA_BYTES = 2048;
25+
private final static int BLOCKLIGHTDATA_BYTES = 2048;
2526

2627
public static void main(String[] args) throws IOException {
2728
//System.out.println((-1 % 32 + 32) % 32);
@@ -91,139 +92,191 @@ public static void convert(File src, File des) throws IOException{
9192
DBIterator iterator = db.iterator();
9293
//ArrayList<byte[]> keys = new ArrayList<byte[]>();
9394
HashMap<String, RegionFile> regions = new HashMap<String, RegionFile>();
95+
HashMap<String, CompoundTag> comChunks = new HashMap<>();
96+
HashMap<String, Integer> chunkHeight = new HashMap<>();
9497
try{
9598
for(iterator.seekToFirst(); iterator.hasNext(); iterator.next()){
9699
byte[] key = iterator.peekNext().getKey();
97-
if(key.length == 9 && key[8] == 0x30){
98-
byte[] value = iterator.peekNext().getValue();
100+
//System.out.println(byte2s(key, false));
101+
byte[] value = iterator.peekNext().getValue();
102+
//System.out.println("Length: " + value.length);
103+
if(key.length == 10 && key[8] == 47){
104+
105+
99106
int chunkX = byteArrayToInt(new byte[]{key[3], key[2], key[1], key[0]});
100107
int chunkZ = byteArrayToInt(new byte[]{key[7], key[6], key[5], key[4]});
101-
System.out.println("Converting chunk X:"+chunkX+" Z:"+chunkZ);
108+
int chunkY = (int) key[9];
109+
110+
System.out.print("\rConverting subchunk X: "+chunkX+" Z: "+chunkZ+" Y: "+chunkY+" ");
111+
System.out.flush();
102112
totalChunk++;
103-
CompoundTag tag = new CompoundTag();
104-
CompoundTag levelData = new CompoundTag();
105-
tag.put("Level", levelData);
106113

107-
levelData.putByte("LightPopulated", (byte)1);
108-
levelData.putByte("TerrainPopulated", (byte)1);
109-
levelData.putByte("V", (byte)1);
110-
levelData.putInt("xPos", chunkX);
111-
levelData.putInt("zPos", chunkZ);
112-
levelData.putLong("InhabitedTime", 0);
113-
levelData.putLong("LastUpdate", 0);
114-
byte[] biomes = new byte[16 * 16];
115-
for(int i = 0; i <256; i++)
116-
biomes[i] = -1;
117-
levelData.putByteArray("Biomes", biomes);
118-
levelData.put("Entities", new ListTag<CompoundTag>("Entities"));
114+
String comKey = chunkX+","+chunkZ;
115+
if (!comChunks.containsKey(comKey)){
116+
//System.out.println("New comChunks");
117+
CompoundTag tag = new CompoundTag();
118+
CompoundTag levelData = new CompoundTag();
119+
tag.put("Level", levelData);
120+
121+
levelData.putByte("LightPopulated", (byte)1);
122+
levelData.putByte("TerrainPopulated", (byte)1);
123+
levelData.putByte("V", (byte)1);
124+
levelData.putInt("xPos", chunkX);
125+
levelData.putInt("zPos", chunkZ);
126+
levelData.putLong("InhabitedTime", 0);
127+
levelData.putLong("LastUpdate", 0);
128+
129+
byte[] biomes = new byte[16 * 16];
130+
for(int i = 0; i <256; i++)
131+
biomes[i] = -1;
132+
levelData.putByteArray("Biomes", biomes);
133+
134+
levelData.put("Entities", new ListTag<CompoundTag>("Entities"));
135+
136+
ListTag<CompoundTag> sectionTags = new ListTag<CompoundTag>("Sections");
137+
levelData.put("Sections", sectionTags);
138+
139+
levelData.put("TileEntities", new ListTag<CompoundTag>("TileEntities"));
140+
141+
comChunks.put(comKey, tag);
142+
}
143+
144+
145+
146+
CompoundTag tag = comChunks.get(comKey);
147+
CompoundTag levelData = tag.getCompound("Level");
119148

120-
ListTag<CompoundTag> sectionTags = new ListTag<CompoundTag>("Sections");
149+
@SuppressWarnings("unchecked")
150+
ListTag<CompoundTag> sectionTags = (ListTag<CompoundTag>) levelData.getList("Sections");
121151

122152
LevelDBChunk data = new LevelDBChunk(chunkX, chunkZ);
123153

154+
int offset = 1;
155+
124156
data.blocks = new byte[BLOCKDATA_BYTES];
125-
System.arraycopy(value, 0, data.blocks, 0, BLOCKDATA_BYTES);
157+
System.arraycopy(value, offset, data.blocks, 0, BLOCKDATA_BYTES);
158+
offset += BLOCKDATA_BYTES;
126159

127160
byte[] metadata = new byte[METADATA_BYTES];
128-
System.arraycopy(value, BLOCKDATA_BYTES, metadata, 0, METADATA_BYTES);
161+
System.arraycopy(value, offset, metadata, 0, METADATA_BYTES);
162+
offset += METADATA_BYTES;
129163
data.data = new OldDataLayer(metadata, DATALAYER_BITS);
130164

131165
byte[] skyLightData = new byte[SKYLIGHTDATA_BYTES];
132-
System.arraycopy(value, BLOCKDATA_BYTES + METADATA_BYTES, skyLightData, 0, SKYLIGHTDATA_BYTES);
166+
if (offset + SKYLIGHTDATA_BYTES < value.length)
167+
System.arraycopy(value, offset, skyLightData, 0, SKYLIGHTDATA_BYTES);
168+
offset += SKYLIGHTDATA_BYTES;
133169
data.skyLight = new OldDataLayer(skyLightData, DATALAYER_BITS);
134170

135171
byte[] blockLightData = new byte[BLOCKLIGHTDATA_BYTES];
136-
System.arraycopy(value, BLOCKDATA_BYTES + METADATA_BYTES + SKYLIGHTDATA_BYTES, blockLightData, 0, BLOCKLIGHTDATA_BYTES);
172+
if (offset + BLOCKLIGHTDATA_BYTES < value.length)
173+
System.arraycopy(value, offset, blockLightData, 0, BLOCKLIGHTDATA_BYTES);
137174
data.blockLight = new OldDataLayer(blockLightData, DATALAYER_BITS);
138175

139-
for (int yBase = 0; yBase < (128 / 16); yBase++) {
140-
141-
// find non-air
142-
boolean allAir = true;
143-
for (int x = 0; x < 16 && allAir; x++) {
144-
for (int y = 0; y < 16 && allAir; y++) {
145-
for (int z = 0; z < 16; z++) {
146-
int pos = (x << 11) | (z << 7) | (y + (yBase << 4));
147-
int block = data.blocks[pos];
148-
if (block != 0) {
149-
allAir = false;
150-
break;
151-
}
152-
}
153-
}
154-
}
176+
byte[] blocks = new byte[16 * 16 * 16];
177+
DataLayer dataValues = new DataLayer(blocks.length, 4);
178+
DataLayer skyLight = new DataLayer(blocks.length, 4);
179+
DataLayer blockLight = new DataLayer(blocks.length, 4);
155180

156-
if (allAir) {
157-
continue;
158-
}
181+
for (int x = 0; x < 16; x++) {
182+
for (int y = 0; y < 16; y++) {
183+
for (int z = 0; z < 16; z++) {
184+
int pos = (x << 8) | (z << 4) | y;
185+
int block = data.blocks[pos];
159186

160-
// build section
161-
byte[] blocks = new byte[16 * 16 * 16];
162-
DataLayer dataValues = new DataLayer(blocks.length, 4);
163-
DataLayer skyLight = new DataLayer(blocks.length, 4);
164-
DataLayer blockLight = new DataLayer(blocks.length, 4);
187+
blocks[(y << 8) | (z << 4) | x] = (byte) (block & 0xff);
188+
dataValues.set(x, y, z, data.data.get(x, y, z));
189+
//skyLight.set(x, y, z, data.skyLight.get(x, y, z));
190+
//blockLight.set(x, y, z, data.blockLight.get(x, y, z));
191+
skyLight.set(x, y, z, 0xf);
192+
blockLight.set(x, y, z, 0xf);
193+
}
194+
}
195+
}
165196

166-
for (int x = 0; x < 16; x++) {
167-
for (int y = 0; y < 16; y++) {
168-
for (int z = 0; z < 16; z++) {
169-
int pos = (x << 11) | (z << 7) | (y + (yBase << 4));
170-
int block = data.blocks[pos];
197+
CompoundTag sectionTag = new CompoundTag();
171198

172-
blocks[(y << 8) | (z << 4) | x] = (byte) (block & 0xff);
173-
dataValues.set(x, y, z, data.data.get(x, y + (yBase << 4), z));
174-
skyLight.set(x, y, z, data.skyLight.get(x, y + (yBase << 4), z));
175-
blockLight.set(x, y, z, data.blockLight.get(x, y + (yBase << 4), z));
176-
}
177-
}
178-
}
199+
sectionTag.putByte("Y", (byte) (chunkY & 0xff));
200+
sectionTag.putByteArray("Blocks", blocks);
201+
sectionTag.putByteArray("Data", dataValues.data);
202+
sectionTag.putByteArray("SkyLight", skyLight.data);
203+
sectionTag.putByteArray("BlockLight", blockLight.data);
179204

180-
CompoundTag sectionTag = new CompoundTag();
181-
182-
sectionTag.putByte("Y", (byte) (yBase & 0xff));
183-
sectionTag.putByteArray("Blocks", blocks);
184-
sectionTag.putByteArray("Data", dataValues.data);
185-
sectionTag.putByteArray("SkyLight", skyLight.data);
186-
sectionTag.putByteArray("BlockLight", blockLight.data);
187-
188-
sectionTags.add(sectionTag);
189-
}
190-
levelData.put("Sections", sectionTags);
205+
sectionTags.add(sectionTag);
191206

192-
levelData.put("TileEntities", new ListTag<CompoundTag>("TileEntities"));
193-
int[] heightMap = new int[256];
194-
for(int x = 0; x < 16; x++){
195-
for(int z = 0; z < 16; z++){
196-
for(int y = 127; y >= 0; y--){
197-
int pos = (x << 11) | (z << 7) | y;
198-
int block = data.blocks[pos];
199-
if(block != 0){
200-
heightMap[(x << 4) | z] = y;
201-
break;
202-
}
203-
}
204-
}
205-
}
206-
levelData.putIntArray("HeightMap", heightMap);
207-
208-
String k = (chunkX >> 5) + "." + (chunkZ >> 5);
209-
if(!regions.containsKey(k)){
210-
regions.put(k, new RegionFile(new File(des, "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca")));
211-
}
212-
RegionFile regionDest = regions.get(k);
213-
int regionX = (chunkX % 32 + 32) % 32;
214-
int regionZ = (chunkZ % 32 + 32) % 32;
215-
/*if(chunkX < 0 || chunkZ < 0){
216-
@SuppressWarnings("unused")
217-
int i = 1+1;
218-
}*/
219-
DataOutputStream chunkDataOutputStream = regionDest.getChunkDataOutputStream(regionX, regionZ);
220-
if(chunkDataOutputStream == null){
221-
System.out.println(chunkX % 32);
222-
System.out.println(chunkZ % 32);
207+
if (!chunkHeight.containsKey(comKey)) {
208+
chunkHeight.put(comKey, chunkY);
209+
}
210+
else {
211+
int temp = chunkHeight.get(comKey);
212+
if (chunkY > temp)
213+
chunkHeight.put(comKey, chunkY);
214+
}
215+
}
216+
}
217+
218+
219+
220+
Iterator<Entry<String, CompoundTag>> iter = comChunks.entrySet().iterator();
221+
while (iter.hasNext()){
222+
Entry<String, CompoundTag> entry = iter.next();
223+
String key = entry.getKey();
224+
225+
CompoundTag tag = entry.getValue();
226+
CompoundTag levelData = tag.getCompound("Level");
227+
@SuppressWarnings("unchecked")
228+
ListTag<CompoundTag> sectionTags = (ListTag<CompoundTag>) levelData.getList("Sections");
229+
int topChunk = chunkHeight.get(key);
230+
231+
for (int i = 0; i < sectionTags.size(); i++) {
232+
CompoundTag subChunk = sectionTags.get(i);
233+
int Y = subChunk.getByte("Y");
234+
if (Y == topChunk) {
235+
DataLayer dataValues = new DataLayer(subChunk.getByteArray("Data"), 4);
236+
237+
int[] heightMap = new int[256];
238+
for(int x = 0; x < 16; x++){
239+
for(int z = 0; z < 16; z++){
240+
for(int y = 15; y >= 0; y--){
241+
int block = dataValues.get(x, y, z);
242+
if(block != 0){
243+
heightMap[(x << 4) | z] = (Y << 4) | y;
244+
break;
245+
}
246+
}
247+
}
248+
}
249+
levelData.putIntArray("HeightMap", heightMap);
250+
break;
223251
}
224-
NbtIo.write(tag, chunkDataOutputStream);
225-
chunkDataOutputStream.close();
226252
}
253+
/*
254+
int[] heightMap = new int[256];
255+
for(int x = 0; x < 16; x++){
256+
for(int z = 0; z < 16; z++){
257+
heightMap[(x << 4) | z] = 0;
258+
}
259+
}
260+
levelData.putIntArray("HeightMap", heightMap);
261+
*/
262+
String[] parts = key.split(",");
263+
int chunkX = Integer.parseInt(parts[0]);
264+
int chunkZ = Integer.parseInt(parts[1]);
265+
266+
String k = (chunkX >> 5) + "." + (chunkZ >> 5);
267+
if(!regions.containsKey(k)){
268+
regions.put(k, new RegionFile(new File(des, "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca")));
269+
}
270+
RegionFile regionDest = regions.get(k);
271+
int regionX = (chunkX % 32 + 32) % 32;
272+
int regionZ = (chunkZ % 32 + 32) % 32;
273+
DataOutputStream chunkDataOutputStream = regionDest.getChunkDataOutputStream(regionX, regionZ);
274+
if(chunkDataOutputStream == null){
275+
System.out.println(chunkX % 32);
276+
System.out.println(chunkZ % 32);
277+
}
278+
NbtIo.write(tag, chunkDataOutputStream);
279+
chunkDataOutputStream.close();
227280
}
228281
}
229282
finally{
@@ -240,7 +293,7 @@ public static void convert(File src, File des) throws IOException{
240293
db.close();
241294
}
242295
if(totalChunk > 0){
243-
System.out.println("Done!");
296+
System.out.println("\nDone! totalSubChunks: " + totalChunk);
244297
}
245298
else{
246299
System.out.println("Oops! It seems that the input data does not contain any valid chunk.");
@@ -275,7 +328,9 @@ public static byte[] intToByteArray(int i){
275328
public static int byteArrayToInt(byte[] bytes){
276329
int value= 0;
277330
for (int i = 0; i < 4; i++){
278-
int shift = (4 - 1 - i) * 8;
331+
if (bytes.length - i < 1)
332+
break;
333+
int shift = (3 - i) * 8;
279334
value += (bytes[i] & 0x000000FF) << shift;
280335
}
281336
return value;

0 commit comments

Comments
 (0)