1818import java .util .Map .Entry ;
1919
2020public 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 ("\r Converting 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 ("\n Done! Converted legacy chunks: " + totalChunk );
283+ }
284+ else {
285+ System .out .println ("\n It 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 ("\n Done! totalSubChunks : " + totalChunk );
502+ System .out .println ("\n Done! 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 ("\n It 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