-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathanalyze_mts_file.lua
261 lines (220 loc) · 8.02 KB
/
analyze_mts_file.lua
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
--[[ taken from src/mg_schematic.cpp:
Minetest Schematic File Format
All values are stored in big-endian byte order.
[u32] signature: 'MTSM'
[u16] version: 3
[u16] size X
[u16] size Y
[u16] size Z
For each Y:
[u8] slice probability value
[Name-ID table] Name ID Mapping Table
[u16] name-id count
For each name-id mapping:
[u16] name length
[u8[] ] name
ZLib deflated {
For each node in schematic: (for z, y, x)
[u16] content
For each node in schematic:
[u8] probability of occurance (param1)
For each node in schematic:
[u8] param2
}
Version changes:
1 - Initial version
2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
3 - Added y-slice probabilities; this allows for variable height structures
--]]
--handle_schematics = {}
-- taken from https://github.com/MirceaKitsune/minetest_mods_structures/blob/master/structures_io.lua (Taokis Sructures I/O mod)
-- gets the size of a structure file
-- nodenames: contains all the node names that are used in the schematic
-- on_constr: lists all the node names for which on_construct has to be called after placement of the schematic
handle_schematics.analyze_mts_file = function( path )
local size = { x = 0, y = 0, z = 0, version = 0 }
local version = 0;
local file, err = save_restore.file_access(path..'.mts', "rb")
if (file == nil) then
return nil
end
--print('[handle_schematics] Analyzing .mts file '..tostring( path..'.mts' ));
--if( not( string.byte )) then
-- print( '[handle_schematics] Error: string.byte undefined.');
-- return nil;
--end
-- thanks to sfan5 for this advanced code that reads the size from schematic files
local read_s16 = function(fi)
return string.byte(fi:read(1)) * 256 + string.byte(fi:read(1))
end
local function get_schematic_size(f)
-- make sure those are the first 4 characters, otherwise this might be a corrupt file
if f:read(4) ~= "MTSM" then
return nil
end
-- advance 2 more characters
local version = read_s16(f); --f:read(2)
-- the next characters here are our size, read them
return read_s16(f), read_s16(f), read_s16(f), version
end
size.x, size.y, size.z, size.version = get_schematic_size(file)
-- read the slice probability for each y value that was introduced in version 3
if( size.version >= 3 ) then
-- the probability is not very intresting for buildings so we just skip it
file:read( size.y );
end
-- this list is not yet used for anything
local nodenames = {};
-- this list is needed for calling on_construct after place_schematic
local on_constr = {};
-- nodes that require after_place_node to be called
local after_place_node = {};
-- after that: read_s16 (2 bytes) to find out how many diffrent nodenames (node_name_count) are present in the file
local node_name_count = read_s16( file );
for i = 1, node_name_count do
-- the length of the next name
local name_length = read_s16( file );
-- the text of the next name
local name_text = file:read( name_length );
table.insert( nodenames, name_text );
local node_def = handle_schematics.node_defined( name_text );
-- in order to get this information, the node has to be defined and loaded
if( node_def and node_def.on_construct) then
table.insert( on_constr, name_text );
end
-- some nodes need after_place_node to be called for initialization
if( node_def and node_def.after_place_node) then
table.insert( after_place_node, name_text );
end
end
local rotated = 0;
local burried = 0;
local parts = path:split('_');
if( parts and #parts > 2 ) then
if( parts[#parts]=="0" or parts[#parts]=="90" or parts[#parts]=="180" or parts[#parts]=="270" ) then
rotated = tonumber( parts[#parts] );
burried = tonumber( parts[ #parts-1 ] );
if( not( burried ) or burried>20 or burried<0) then
burried = 0;
end
end
end
-- decompression was recently added; if it is not yet present, we need to use normal place_schematic
if( minetest.decompress == nil) then
file.close(file);
return nil; -- normal place_schematic is no longer supported as minetest.decompress is now part of the release version of minetest
-- return { size = { x=size.x, y=size.y, z=size.z}, nodenames = nodenames, on_constr = on_constr, after_place_node = after_place_node, rotated=rotated, burried=burried, scm_data_cache = nil };
end
local compressed_data = file:read( "*all" );
local data_string = minetest.decompress(compressed_data, "deflate" );
file.close(file)
-- find out which id air has in this particular schematic
local is_air = 0;
for i,v in ipairs( nodenames ) do
if( v == 'air' ) then
is_air = i;
end
end
-- some mods (like mg_villages) might be intrested in the number of npc that can live here
local bed_count = 0;
local bed_list = {};
local p2offset = (size.x*size.y*size.z)*3;
local i = 1;
local scm = {};
for z = 1, size.z do
for y = 1, size.y do
for x = 1, size.x do
if( not( scm[y] )) then
scm[y] = {};
end
if( not( scm[y][x] )) then
scm[y][x] = {};
end
local id = string.byte( data_string, i ) * 256 + string.byte( data_string, i+1 );
i = i + 2;
local p2 = string.byte( data_string, p2offset + math.floor(i/2));
id = id+1;
if( id ~= is_air ) then
scm[y][x][z] = {id, p2};
if( handle_schematics.bed_node_names[ nodenames[ id ]]) then
bed_count = bed_count + 1;
table.insert( bed_list, {x=x, y=y, z=z, p2, id});
end
end
end
end
end
--print( "MTS FILE "..tostring(path)..": "..tostring( bed_count ).." beds.");
return { size = { x=size.x, y=size.y, z=size.z}, nodenames = nodenames, on_constr = on_constr, after_place_node = after_place_node, rotated=rotated, burried=burried, scm_data_cache = scm, bed_count = bed_count, bed_list = bed_list };
end
handle_schematics.store_mts_file = function( path, data )
data.nodenames[ #data.nodenames+1 ] = 'air';
local file, err = save_restore.file_access(path..'.mts', "wb")
if (file == nil) then
return nil
end
local write_s16 = function( fi, a )
fi:write( string.char( math.floor( a/256) ));
fi:write( string.char( a%256 ));
end
data.size.version = 3; -- we only support version 3 of the .mts file format
file:write( "MTSM" );
write_s16( file, data.size.version );
write_s16( file, data.size.x );
write_s16( file, data.size.y );
write_s16( file, data.size.z );
-- set the slice probability for each y value that was introduced in version 3
if( data.size.version >= 3 ) then
-- the probability is not very intresting for buildings so we just skip it
for i=1,data.size.y do
file:write( string.char(255) );
end
end
-- set how many diffrent nodenames (node_name_count) are present in the file
write_s16( file, #data.nodenames );
for i = 1, #data.nodenames do
-- the length of the next name
write_s16( file, string.len( data.nodenames[ i ] ));
file:write( data.nodenames[ i ] );
end
-- this string will later be compressed
local node_data = "";
-- actual node data
for z = 1, data.size.z do
for y = 1, data.size.y do
for x = 1, data.size.x do
local a = data.scm_data_cache[y][x][z];
if( a and type( a ) == 'table') then
node_data = node_data..string.char( math.floor( a[1]/256) )..string.char( a[1]%256-1);
else
node_data = node_data..string.char( 0 )..string.char( #data.nodenames-1 );
end
end
end
end
-- probability of occurance
for z = 1, data.size.z do
for y = 1, data.size.y do
for x = 1, data.size.x do
node_data = node_data..string.char( 255 );
end
end
end
-- param2
for z = 1, data.size.z do
for y = 1, data.size.y do
for x = 1, data.size.x do
local a = data.scm_data_cache[y][x][z];
if( a and type( a) == 'table' ) then
node_data = node_data..string.char( a[2] );
else
node_data = node_data..string.char( 0 );
end
end
end
end
local compressed_data = minetest.compress( node_data, "deflate" );
file:write( compressed_data );
file.close(file);
print('SAVING '..path..'.mts (converted from .we).');
end