1 /*
2  * Copyright (c) 2017-2020 sel-project
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  *
22  */
23 /**
24  * Copyright: Copyright (c) 2017-2020 sel-project
25  * License: MIT
26  * Authors: Kripth
27  * Source: $(HTTP github.com/sel-project/sel-level/sel/level/format/leveldb.d, sel/level/format/leveldb.d)
28  */
29 module sel.level.format.leveldb;
30 
31 import std.file : exists, isFile, read, write, dirEntries, SpanMode, FileException;
32 import std..string : endsWith;
33 import std.typetuple : TypeTuple;
34 import std.zlib : Compress, UnCompress;
35 
36 import sel.level.data;
37 import sel.level.exception;
38 import sel.level.level;
39 
40 import sel.math : Vector2;
41 
42 import sel.nbt.file : PocketLevelFormat;
43 import sel.nbt.tags;
44 
45 import std.stdio : writeln; // debug
46 
47 private alias LevelInfoValues = TypeTuple!(
48 	String, "name", "LevelName",
49 	Long, "seed", "RandomSeed",
50 	Int, "gamemode", "GameType",
51 	Int, "difficulty", "Difficulty",
52 	Long, "time", "Time",
53 	Int, "spawn.x", "SpawnX",
54 	Int, "spawn.y", "SpawnY",
55 	Int, "spawn.z", "SpawnZ",
56 	Byte, "raining", "rainLevel",
57 	Int, "rainTime", "rainTime",
58 	Byte, "thundering", "thunderLevel",
59 	Int, "thunderTime", "thunderTime",
60 	Byte, "commandsAllowed", "commandsEnabled",
61 );
62 
63 class LevelDB : Level {
64 
65 	private PocketLevelFormat infoReader;
66 
67 	public this(string path) {
68 		super(path);
69 		this.infoReader = new PocketLevelFormat(this.path ~ "level.dat");
70 	}
71 
72 	protected override LevelInfo readLevelInfo() {
73 		Compound compound;
74 		try {
75 			compound = this.infoReader.load();
76 		} catch(FileException) {
77 			throw new LevelInfoException(LevelInfoException.NOT_FOUND, "Level info was not found");
78 		}
79 		enforceLevelInfoException(compound !is null, LevelInfoException.WRONG_FORMAT, "Root tag is not a compound");
80 		return readLevelInfoCompound!LevelInfoValues(compound);
81 	}
82 
83 	protected override void writeLevelInfo(LevelInfo levelInfo) {
84 
85 	}
86 
87 	protected override Chunk readChunkImpl(Dimension dimension, Vector2!int position) {
88 		return null;
89 	}
90 
91 	protected override ReadChunksResult readChunksImpl(Dimension dimension) {
92 		// uncompress and copy to uc
93 		/*foreach(string file ; dirEntries(this.path ~ "db", SpanMode.shallow)) {
94 			if(file.isFile && file.endsWith(".ldb")) {
95 				UnCompress uncompress = new UnCompress();
96 				const(void)[] data = uncompress.uncompress(read(file));
97 				data ~= uncompress.flush();
98 				write(this.path ~ "uncompressed_db" ~ file[this.path.length+2..$], data);
99 			}
100 		}*/
101 		return ReadChunksResult.init;
102 	}
103 
104 }
105 
106 unittest {
107 
108 	auto level = new LevelDB("test/LevelDB");
109 
110 	with(level.levelInfo) {
111 		assert(name == "My World");
112 		assert(seed == 2354673524L);
113 		assert(gamemode == 1);
114 		assert(difficulty == 2);
115 		assert(spawn.x == 152);
116 		assert(spawn.y == 32767);
117 		assert(spawn.z == 4);
118 	}
119 
120 	level.readChunks();
121 
122 }