1 module ungmk
is aliced
;
11 //version = gmk_debug_dump;
14 // ////////////////////////////////////////////////////////////////////////// //
15 static immutable string
[uint] evKeyNames
;
16 static immutable string
[uint] evMouseNames
;
17 static immutable string
[uint] evOtherNames
;
19 shared static this () {
21 evKeyNames
[37] = "left";
22 evKeyNames
[39] = "right";
23 evKeyNames
[38] = "up";
24 evKeyNames
[40] = "down";
26 evKeyNames
[17] = "control";
27 evKeyNames
[18] = "alt";
28 evKeyNames
[16] = "shift";
29 evKeyNames
[32] = "space";
30 evKeyNames
[13] = "enter";
32 evKeyNames
[96] = "numpad 0";
33 evKeyNames
[97] = "numpad 1";
34 evKeyNames
[98] = "numpad 2";
35 evKeyNames
[99] = "numpad 3";
36 evKeyNames
[100] = "numpad 4";
37 evKeyNames
[101] = "numpad 5";
38 evKeyNames
[102] = "numpad 6";
39 evKeyNames
[103] = "numpad 7";
40 evKeyNames
[104] = "numpad 8";
41 evKeyNames
[105] = "numpad 9";
43 evKeyNames
[111] = "numpad divide";
44 evKeyNames
[106] = "numpad multiply";
45 evKeyNames
[109] = "numpad subtract";
46 evKeyNames
[107] = "numpad add";
47 evKeyNames
[110] = "numpad decimal";
87 evKeyNames
[112] = "f1";
88 evKeyNames
[113] = "f2";
89 evKeyNames
[114] = "f3";
90 evKeyNames
[115] = "f4";
91 evKeyNames
[116] = "f5";
92 evKeyNames
[117] = "f6";
93 evKeyNames
[118] = "f7";
94 evKeyNames
[119] = "f8";
95 evKeyNames
[120] = "f9";
96 evKeyNames
[121] = "f10";
97 evKeyNames
[122] = "f11";
98 evKeyNames
[123] = "f12";
100 evKeyNames
[8] = "backspace";
101 evKeyNames
[27] = "escape";
102 evKeyNames
[36] = "home";
103 evKeyNames
[35] = "end";
104 evKeyNames
[33] = "pageup";
105 evKeyNames
[34] = "pagedown";
106 evKeyNames
[46] = "delete";
107 evKeyNames
[45] = "insert";
109 evKeyNames
[0] = "no key";
110 evKeyNames
[1] = "any key";
113 evMouseNames
[0] = "mouse left button";
114 evMouseNames
[1] = "mouse right button";
115 evMouseNames
[2] = "mouse middle button";
116 evMouseNames
[3] = "mouse no button";
117 evMouseNames
[4] = "mouse left button pressed";
118 evMouseNames
[5] = "mouse right button pressed";
119 evMouseNames
[6] = "mouse middle button pressed";
120 evMouseNames
[7] = "mouse left button released";
121 evMouseNames
[8] = "mouse right button released";
122 evMouseNames
[9] = "mouse middle button released";
123 evMouseNames
[10] = "mouse enter";
124 evMouseNames
[11] = "mouse leave";
125 evMouseNames
[16] = "joystick 1 left";
126 evMouseNames
[17] = "joystick 1 right";
127 evMouseNames
[18] = "joystick 1 up";
128 evMouseNames
[19] = "joystick 1 down";
129 evMouseNames
[21] = "joystick 1 button 1";
130 evMouseNames
[22] = "joystick 1 button 2";
131 evMouseNames
[23] = "joystick 1 button 3";
132 evMouseNames
[24] = "joystick 1 button 4";
133 evMouseNames
[25] = "joystick 1 button 5";
134 evMouseNames
[26] = "joystick 1 button 6";
135 evMouseNames
[27] = "joystick 1 button 7";
136 evMouseNames
[28] = "joystick 1 button 8";
137 evMouseNames
[31] = "joystick 2 left";
138 evMouseNames
[32] = "joystick 2 right";
139 evMouseNames
[33] = "joystick 2 up";
140 evMouseNames
[34] = "joystick 2 down";
141 evMouseNames
[36] = "joystick 2 button 1";
142 evMouseNames
[37] = "joystick 2 button 2";
143 evMouseNames
[38] = "joystick 2 button 3";
144 evMouseNames
[39] = "joystick 2 button 4";
145 evMouseNames
[40] = "joystick 2 button 5";
146 evMouseNames
[41] = "joystick 2 button 6";
147 evMouseNames
[42] = "joystick 2 button 7";
148 evMouseNames
[43] = "joystick 2 button 8";
149 evMouseNames
[50] = "mouse global left button";
150 evMouseNames
[51] = "mouse global right button";
151 evMouseNames
[52] = "mouse global middle button";
152 evMouseNames
[53] = "mouse global left pressed";
153 evMouseNames
[54] = "mouse global right pressed";
154 evMouseNames
[55] = "mouse global middle pressed";
155 evMouseNames
[56] = "mouse global left released";
156 evMouseNames
[57] = "mouse global right released";
157 evMouseNames
[58] = "mouse global middle released";
158 evMouseNames
[60] = "mouse wheel up";
159 evMouseNames
[61] = "mouse wheel down";
162 evOtherNames
[0] = "outside room";
163 evOtherNames
[1] = "intersect boundary";
164 evOtherNames
[2] = "game start";
165 evOtherNames
[3] = "game end";
166 evOtherNames
[4] = "room start";
167 evOtherNames
[5] = "room end";
168 evOtherNames
[6] = "no more lives";
169 evOtherNames
[7] = "animation end";
170 evOtherNames
[8] = "end of path";
171 evOtherNames
[9] = "no more health";
173 evOtherNames
[10] = "user 0";
174 evOtherNames
[11] = "user 1";
175 evOtherNames
[12] = "user 2";
176 evOtherNames
[13] = "user 3";
177 evOtherNames
[14] = "user 4";
178 evOtherNames
[15] = "user 5";
179 evOtherNames
[16] = "user 6";
180 evOtherNames
[17] = "user 7";
181 evOtherNames
[18] = "user 8";
182 evOtherNames
[19] = "user 9";
183 evOtherNames
[20] = "user 10";
184 evOtherNames
[21] = "user 11";
185 evOtherNames
[22] = "user 12";
186 evOtherNames
[23] = "user 13";
187 evOtherNames
[24] = "user 14";
188 evOtherNames
[25] = "user 15";
190 evOtherNames
[30] = "close button";
192 evOtherNames
[40] = "outside view 0";
193 evOtherNames
[41] = "outside view 1";
194 evOtherNames
[42] = "outside view 2";
195 evOtherNames
[43] = "outside view 3";
196 evOtherNames
[44] = "outside view 4";
197 evOtherNames
[45] = "outside view 5";
198 evOtherNames
[46] = "outside view 6";
199 evOtherNames
[47] = "outside view 7";
201 evOtherNames
[50] = "boundary view 0";
202 evOtherNames
[51] = "boundary view 1";
203 evOtherNames
[52] = "boundary view 2";
204 evOtherNames
[53] = "boundary view 3";
205 evOtherNames
[54] = "boundary view 4";
206 evOtherNames
[55] = "boundary view 5";
207 evOtherNames
[56] = "boundary view 6";
208 evOtherNames
[57] = "boundary view 7";
212 // ////////////////////////////////////////////////////////////////////////// //
213 DateTime
fromDelphiDT (double d
) {
215 import core
.stdc
.math
: modf
;
216 auto dt = Date(Date(1899, 12, 30).dayOfGregorianCal
+cast(int)d
);
219 auto seconds
= cast(int)(24*60*60*f
+0.5);
220 if (seconds
> 59) seconds
= 59;
221 auto tm
= TimeOfDay(seconds
/(60*60), (seconds
/60)%60, seconds
%60);
222 return DateTime(dt, tm
);
226 double toDelphiDT (DateTime
dt) {
227 double d
= dt.dayOfGregorianCal
-Date(1899, 12, 30).dayOfGregorianCal
;
228 d
+= (dt.hour
*(60*60)+dt.minute
*60+dt.second
)/(24.0*60.0*60.0);
233 // ////////////////////////////////////////////////////////////////////////// //
234 string
readPStr (VFile fl
) {
235 auto sz
= fl
.readNum
!uint;
236 if (sz
> 1024*1024) throw new Exception("string too long");
237 if (sz
== 0) return "";
238 auto res
= new char[](sz
);
239 fl
.rawReadExact(res
[]);
240 return cast(string
)res
; // it's safe here
244 void writePStr (VFile fl
, string s
) {
245 if (s
.length
> 1024*1024) throw new Exception("string too long");
246 fl
.writeNum
!uint(cast(uint)s
.length
);
247 if (s
.length
) fl
.rawWriteExact(s
[]);
252 DateTime
readDateTime (VFile fl
) {
253 auto d
= fl
.readNum
!double;
255 auto dt = fromDelphiDT(d
);
256 auto dd = toDelphiDT(dt);
257 auto xd
= fromDelphiDT(dd);
262 return fromDelphiDT(d
);
266 void writeDateTime (VFile fl
, DateTime
dt) {
267 auto d
= toDelphiDT(dt);
268 fl
.writeNum
!double(d
);
272 // ////////////////////////////////////////////////////////////////////////// //
273 TrueColorImage
readImage (VFile fl
) {
274 auto w
= fl
.readNum
!uint;
275 auto h
= fl
.readNum
!uint;
276 if (w
> 32767 || h
> 32767) {
277 import std
.conv
: to
;
278 throw new Exception("too big image: "~to
!string(w
)~"x"~to
!string(h
));
280 if (w
> 0 && h
> 0) {
281 auto isz
= fl
.readNum
!uint;
282 if (isz
< w
*h
*4) throw new Exception("image data size too small");
284 auto img
= new TrueColorImage(w
, h
);
285 fl
.rawReadExact(img
.imageData
.bytes
[0..w
*h
*4]);
287 auto bp
= img
.imageData
.bytes
.ptr
;
288 foreach (immutable _
; 0..w
*h
) {
294 //while (isz--) fl.readNum!ubyte;
295 if (isz
> 0) fl
.seek(isz
, Seek
.Cur
);
298 return new TrueColorImage(0, 0);
303 void writeImage (VFile fl
, TrueColorImage img
) {
304 if (img
.width
> 32767 || img
.height
> 32767) throw new Exception("too big image");
305 fl
.writeNum
!uint(cast(uint)img
.width
);
306 fl
.writeNum
!uint(cast(uint)img
.height
);
307 if (img
.width
> 0 && img
.height
> 0) {
308 fl
.writeNum
!uint(cast(uint)img
.width
*cast(uint)img
.height
*4);
309 foreach (int y
; 0..img
.height
) {
310 foreach (int x
; 0..img
.width
) {
311 Color clr
= img
.getPixel(x
, y
);
312 fl
.writeNum
!ubyte(clr
.b
);
313 fl
.writeNum
!ubyte(clr
.g
);
314 fl
.writeNum
!ubyte(clr
.r
);
315 fl
.writeNum
!ubyte(clr
.a
);
322 // ////////////////////////////////////////////////////////////////////////// //
323 struct XField(string typename
) {
324 enum TypeName
= typename
;
329 // ////////////////////////////////////////////////////////////////////////// //
330 mixin template GenIO() {
331 private static alias Id(alias T
) = T
;
333 private static string
genRead(T
) () {
335 foreach (immutable fname
; __traits(derivedMembers
, T
)) {
336 alias mem
= Id
!(__traits(getMember
, T
, fname
));
337 foreach (immutable uda
; __traits(getAttributes
, mem
)) {
338 static if (typeof(uda
).stringof
.length
> 6 && typeof(uda
).stringof
[0..7] == "XField!") {
339 static if (uda
.TypeName
[0] == '!') {
341 res
~= "read_"~fname
~"(fl);\n";
342 } else static if (uda
.TypeName
== "Color") {
343 res
~= fname
~".b = fl.readNum!ubyte;\n";
344 res
~= fname
~".g = fl.readNum!ubyte;\n";
345 res
~= fname
~".r = fl.readNum!ubyte;\n";
346 res
~= fname
~".a = fl.readNum!ubyte;\n";
347 } else static if (uda
.TypeName
== "pstr") {
348 res
~= fname
~" = fl.readPStr;\n";
349 } else static if (uda
.TypeName
== "tdatetime") {
350 res
~= fname
~" = fl.readDateTime;\n";
351 } else static if (typeof(mem
).stringof
== uda
.TypeName
) {
352 res
~= fname
~" = fl.readNum!"~uda
.TypeName
~";\n";
353 } else static if (is(typeof(mem
) == bool)) {
354 res
~= fname
~" = (fl.readNum!"~uda
.TypeName
~" != 0);\n";
356 res
~= fname
~" = cast("~typeof(mem
).stringof
~")fl.readNum!"~uda
.TypeName
~";\n";
357 //static assert(0, "wtf for '"~fname~"': uda says '"~uda.TypeName~"', type is '"~typeof(mem).stringof~"'");
365 private static string
genWrite(T
) () {
367 foreach (immutable fname
; __traits(derivedMembers
, T
)) {
368 alias mem
= Id
!(__traits(getMember
, T
, fname
));
369 foreach (immutable uda
; __traits(getAttributes
, mem
)) {
370 static if (typeof(uda
).stringof
.length
> 6 && typeof(uda
).stringof
[0..7] == "XField!") {
371 static if (uda
.TypeName
[0] == '!') {
373 res
~= "write_"~fname
~"(fl);\n";
374 } else static if (uda
.TypeName
== "Color") {
375 res
~= "fl.writeNum!ubyte("~fname
~".b);\n";
376 res
~= "fl.writeNum!ubyte("~fname
~".g);\n";
377 res
~= "fl.writeNum!ubyte("~fname
~".r);\n";
378 res
~= "fl.writeNum!ubyte("~fname
~".a);\n";
379 } else static if (uda
.TypeName
== "pstr") {
380 res
~= "fl.writePStr("~fname
~");\n";
381 } else static if (uda
.TypeName
== "tdatetime") {
382 res
~= "fl.writeDateTime("~fname
~");\n";
383 } else static if (typeof(mem
).stringof
== uda
.TypeName
) {
384 res
~= "fl.writeNum!"~uda
.TypeName
~"("~fname
~");\n";
385 } else static if (is(typeof(mem
) == bool)) {
386 res
~= "fl.writeNum!"~uda
.TypeName
~"(cast("~uda
.TypeName
~")("~fname
~" ? 1 : 0));\n";
388 //static assert(0, "wtf for '"~fname~"'!");
389 res
~= "fl.writeNum!"~uda
.TypeName
~"(cast("~uda
.TypeName
~")"~fname
~");\n";
398 private static string genDump(T) () {
400 foreach (immutable fname; __traits(derivedMembers, T)) {
401 alias mem = Id!(__traits(getMember, T, fname));
402 foreach (immutable uda; __traits(getAttributes, mem)) {
403 static if (uda.TypeName[0] == '!') {
405 } else static if (typeof(uda).stringof.length > 6 && typeof(uda).stringof[0..7] == "XField!") {
406 static if (uda.TypeName == "Color") {
407 res ~= "writefln(\""~uda.desc~": rgba(%s,%s,%s,%s)\", "~fname~".r, "~fname~".g, "~fname~".b, "~fname~".a);\n";
408 } else static if (uda.TypeName == "pstr") {
409 res ~= "writeln(\""~uda.desc~": [\", "~fname~", \"]\");\n";
410 } else static if (uda.TypeName == "tdatetime") {
411 //res ~= "writeln(\""~uda.desc~": \", "~fname~");\n";
413 res ~= "writeln(\""~uda.desc~": \", "~fname~");\n";
422 //pragma(msg, genRead!(typeof(this)));
423 //pragma(msg, genWrite!(typeof(this)));
424 //pragma(msg, genDump!(typeof(this)));
425 mixin("void read (VFile fl) {\n"~genRead
!(typeof(this))~"}");
426 mixin("void write (VFile fl) {\n"~genWrite
!(typeof(this))~"}");
427 //mixin("void dump () {\nimport std.stdio;\n"~genDump!(typeof(this))~"}");
431 // ////////////////////////////////////////////////////////////////////////// //
432 class GMSomething
{ this () pure nothrow @safe @nogc {} }
435 // ////////////////////////////////////////////////////////////////////////// //
436 final class GMSprite
: GMSomething
{
438 enum Shape
{ Precise
, Rectangle
, Disk
, Diamond
}
439 enum BBox
{ Automatic
, Full
, Manual
}
446 TrueColorImage
[] images
;
448 ubyte alphaTolerance
;
449 bool separateCollisionMasks
;
451 int bbleft
, bbtop
, bbright
, bbbottom
;
453 this (uint aidx
, VFile fl
) { idx
= aidx
; load(fl
); }
456 void load (VFile fl
) {
458 lastmod
= fl
.readDateTime
;
459 auto xver
= fl
.readNum
!uint;
460 //writeln("xver: ", xver);
461 if (xver
< 800 || xver
> 810) throw new Exception("invalid sprite version");
462 xofs
= fl
.readNum
!int;
463 yofs
= fl
.readNum
!int;
464 auto count
= fl
.readNum
!uint;
465 if (count
> 65535) throw new Exception("too many images in sprite sprite");
467 foreach (immutable idx
; 0..count
) {
468 xver
= fl
.readNum
!uint;
469 if (xver
< 800) throw new Exception("invalid sprite image version");
470 images
~= fl
.readImage
;
473 auto v
= fl
.readNum
!uint;
474 if (v
> Shape
.max
) throw new Exception("invalid sprite shape");
475 shape
= cast(Shape
)v
;
478 auto v
= fl
.readNum
!uint;
479 if (v
> alphaTolerance
.max
) throw new Exception("invalid sprite alpha tolerance");
480 alphaTolerance
= cast(ubyte)v
;
482 separateCollisionMasks
= (fl
.readNum
!uint != 0);
484 auto v
= fl
.readNum
!uint;
485 if (v
> BBox
.max
) throw new Exception("invalid sprite bounding box type");
488 bbleft
= fl
.readNum
!int;
489 bbright
= fl
.readNum
!int;
490 bbbottom
= fl
.readNum
!int;
491 bbtop
= fl
.readNum
!int;
496 // ////////////////////////////////////////////////////////////////////////// //
497 final class GMBackground
: GMSomething
{
503 int tileWidth
, tileHeight
;
506 TrueColorImage image
;
508 this (uint aidx
, VFile fl
) { idx
= aidx
; load(fl
); }
511 void load (VFile fl
) {
513 lastmod
= fl
.readDateTime
;
514 auto xver
= fl
.readNum
!uint;
515 //writeln("xver: ", xver);
516 if (xver
!= 710) throw new Exception("invalid background version");
517 tileset
= (fl
.readNum
!uint != 0);
518 tileWidth
= fl
.readNum
!int;
519 tileHeight
= fl
.readNum
!int;
520 xofs
= fl
.readNum
!int;
521 yofs
= fl
.readNum
!int;
522 xsep
= fl
.readNum
!int;
523 ysep
= fl
.readNum
!int;
524 xver
= fl
.readNum
!uint;
525 //writeln("xver: ", xver);
526 if (xver
< 800 || xver
> 810) throw new Exception("invalid background info version");
527 image
= fl
.readImage
;
532 // ////////////////////////////////////////////////////////////////////////// //
533 final class GMAction
: GMSomething
{
535 enum Type
{ Nothing
, Function
, Code
}
582 int applyobj
; // object index to apply; -1: self; -2: other
588 this (VFile fl
) { load(fl
); }
591 void load (VFile fl
) {
592 auto xver
= fl
.readNum
!uint;
593 if (xver
!= 440) throw new Exception("invalid action version");
594 libid
= fl
.readNum
!uint;
595 id
= fl
.readNum
!uint;
597 auto v
= fl
.readNum
!uint;
598 if (v
> Kind
.max
) throw new Exception("invalid action kind");
601 mayberelative
= (fl
.readNum
!uint != 0);
602 question
= (fl
.readNum
!uint != 0);
603 applied
= (fl
.readNum
!uint != 0);
605 auto v
= fl
.readNum
!uint;
606 if (v
> Type
.max
) throw new Exception("invalid action type");
609 funcname
= fl
.readPStr
;
610 codename
= fl
.readPStr
;
611 argused
= fl
.readNum
!uint;
612 if (argused
> 8) throw new Exception("invalid number of arguments used in action");
613 auto akcount
= fl
.readNum
!uint;
614 if (akcount
> 8) { import std
.conv
: to
; throw new Exception("invalid number of argument kinds for action: "~to
!string(akcount
)); }
615 argtypes
[] = ArgType
.t_string
; //FIXME
616 foreach (immutable idx
; 0..akcount
) {
617 auto v
= fl
.readNum
!uint;
618 if (v
> ArgType
.max
) throw new Exception("invalid argument type");
619 argtypes
[idx
] = cast(ArgType
)v
;
621 applyobj
= fl
.readNum
!int;
622 relative
= fl
.readNum
!uint;
623 auto akvals
= fl
.readNum
!uint;
624 if (akvals
!= akcount
) { import std
.conv
: to
; throw new Exception("invalid number of argument values for action: "~to
!string(akvals
)); }
626 foreach (immutable idx
; 0..akvals
) argvals
[idx
] = fl
.readPStr
;
627 negated
= (fl
.readNum
!uint != 0);
632 // ////////////////////////////////////////////////////////////////////////// //
633 final class GMEvent
: GMSomething
{
655 this (VFile fl
, Type atype
, uint aid
) { type
= atype
; load(fl
, aid
); }
658 void load (VFile fl
, uint aid
) {
660 auto xver
= fl
.readNum
!uint;
661 if (xver
!= 400) throw new Exception("invalid event version");
662 auto count
= fl
.readNum
!uint;
663 if (count
> 1024) throw new Exception("too many actions in event");
664 foreach (immutable idx
; 0..count
) actions
~= new GMAction(fl
);
669 // ////////////////////////////////////////////////////////////////////////// //
670 final class GMObject
: GMSomething
{
675 int spridx
; // -1: none
680 int parentobjidx
; // -100: none
681 int maskspridx
; // -1: none
682 GMEvent
[][GMEvent
.Type
.max
+1] events
;
685 this (uint aidx
, VFile fl
) { idx
= aidx
; load(fl
); }
688 void load (VFile fl
) {
690 lastmod
= fl
.readDateTime
;
691 auto xver
= fl
.readNum
!uint;
692 if (xver
!= 430) throw new Exception("invalid object version");
693 spridx
= fl
.readNum
!int;
694 solid
= (fl
.readNum
!uint != 0);
695 visible
= (fl
.readNum
!uint != 0);
696 depth
= fl
.readNum
!int;
697 persistent
= (fl
.readNum
!uint != 0);
698 parentobjidx
= fl
.readNum
!int;
699 maskspridx
= fl
.readNum
!int;
700 auto ecount
= fl
.readNum
!uint;
701 if (ecount
!= 10 && ecount
!= 11) throw new Exception("invalid number of event types");
702 foreach (immutable evidx
; 0..ecount
+1) {
705 int eid
= fl
.readNum
!int;
706 if (eid
== -1) break;
707 lst
~= new GMEvent(fl
, cast(GMEvent
.Type
)evidx
, eid
);
715 // ////////////////////////////////////////////////////////////////////////// //
716 final class GMRoom
: GMSomething
{
722 int bgimageidx
; // -1: none
729 void load (VFile fl
) {
730 visibleOnStart
= (fl
.readNum
!uint != 0);
731 fgimage
= (fl
.readNum
!uint != 0);
732 bgimageidx
= fl
.readNum
!int;
735 xtile
= fl
.readNum
!int;
736 ytile
= fl
.readNum
!int;
737 xspeed
= fl
.readNum
!int;
738 yspeed
= fl
.readNum
!int;
739 stretch
= (fl
.readNum
!uint != 0);
750 int xborder
, yborder
;
752 int objfollow
; // -1: none
755 void load (VFile fl
, uint xver
) {
756 visibleOnStart
= (fl
.readNum
!uint != 0);
760 width
= fl
.readNum
!int;
761 height
= fl
.readNum
!int;
762 portx
= fl
.readNum
!int;
763 porty
= fl
.readNum
!int;
768 width
= fl
.readNum
!int;
769 height
= fl
.readNum
!int;
770 portx
= fl
.readNum
!int;
771 porty
= fl
.readNum
!int;
772 portw
= fl
.readNum
!int;
773 porth
= fl
.readNum
!int;
775 xborder
= fl
.readNum
!int;
776 yborder
= fl
.readNum
!int;
777 xspace
= fl
.readNum
!int;
778 yspace
= fl
.readNum
!int;
779 objfollow
= fl
.readNum
!int;
783 static final class Inst
{
792 void load (VFile fl
) {
795 objidx
= fl
.readNum
!int;
796 id
= fl
.readNum
!uint;
797 createcode
= fl
.readPStr
;
798 locked
= (fl
.readNum
!uint != 0);
802 static final class Tile
{
813 void load (VFile fl
) {
816 bgidx
= fl
.readNum
!int;
817 xtile
= fl
.readNum
!int;
818 ytile
= fl
.readNum
!int;
819 wtile
= fl
.readNum
!int;
820 htile
= fl
.readNum
!int;
821 layer
= fl
.readNum
!int;
822 id
= fl
.readNum
!uint;
823 locked
= (fl
.readNum
!uint != 0);
850 this (uint aidx
, VFile fl
) { idx
= aidx
; load(fl
); }
853 void load (VFile fl
) {
855 lastmod
= fl
.readDateTime
;
856 auto xver
= fl
.readNum
!uint;
857 if (xver
!= 520 && xver
!= 541) throw new Exception("invalid room version");
858 caption
= fl
.readPStr
;
859 width
= fl
.readNum
!uint;
860 height
= fl
.readNum
!uint;
861 xsnap
= fl
.readNum
!int;
862 ysnap
= fl
.readNum
!int;
863 isogrid
= (fl
.readNum
!uint != 0);
864 speed
= fl
.readNum
!int;
865 persistent
= (fl
.readNum
!uint != 0);
866 bgcolor
= fl
.readNum
!uint;
867 drawbgcolor
= (fl
.readNum
!uint != 0);
868 createcode
= fl
.readPStr
;
869 if (fl
.readNum
!uint != 8) throw new Exception("invalid number of backgrounds in room");
870 foreach (ref Back b
; backs
) b
.load(fl
);
871 viewsEnabled
= (fl
.readNum
!uint != 0);
872 if (fl
.readNum
!uint != 8) throw new Exception("invalid number of views in room");
873 foreach (ref View v
; views
) v
.load(fl
, xver
);
874 auto count
= fl
.readNum
!uint;
875 if (count
> 1024*1024) throw new Exception("too many instances in room");
876 foreach (immutable idx
; 0..count
) { auto i
= new Inst(); i
.load(fl
); insts
~= i
; }
877 count
= fl
.readNum
!uint;
878 if (count
> 1024*1024) throw new Exception("too many tiles in room");
879 foreach (immutable idx
; 0..count
) { auto t
= new Tile
; t
.load(fl
); tiles
~= t
; }
880 bool rei
= (fl
.readNum
!uint != 0); // room editor info
882 fl
.readNum
!uint; // REI width
883 fl
.readNum
!uint; // REI height
884 fl
.readNum
!uint; // REI grid show bool
885 fl
.readNum
!uint; // REI objects show bool
886 fl
.readNum
!uint; // REI tiles show bool
887 fl
.readNum
!uint; // REI bg show bool
888 fl
.readNum
!uint; // REI fg show bool
889 fl
.readNum
!uint; // REI views show bool
890 fl
.readNum
!uint; // REI delete underlying objects bool
891 fl
.readNum
!uint; // REI delete underlying tiles bool
893 tilew
= fl
.readNum
!int;
894 tileh
= fl
.readNum
!int;
895 xtsep
= fl
.readNum
!int;
896 ytsep
= fl
.readNum
!int;
897 xtofs
= fl
.readNum
!int;
898 ytofs
= fl
.readNum
!int;
904 fl
.readNum
!uint; // REI tab
905 fl
.readNum
!uint; // REI scroll x
906 fl
.readNum
!uint; // REI scroll y
911 // ////////////////////////////////////////////////////////////////////////// //
912 final class GMTrigger
: GMSomething
{
914 enum When
{ Begin
, Middle
, End
}
924 this (uint aidx
, VFile fl
) { idx
= aidx
; load(fl
); }
927 void load (VFile fl
) {
928 auto xver
= fl
.readNum
!uint;
930 if (xver
!= 800) throw new Exception("invalid trigger version");
932 condition
= fl
.readPStr
;
934 auto v
= fl
.readNum
!uint;
935 if (v
> When
.max
) throw new Exception("invalid trigger when");
938 constname
= fl
.readPStr
;
943 // ////////////////////////////////////////////////////////////////////////// //
944 final class GMGameSettings
: GMSomething
{
945 @XField!("uint")("fullscreen") bool fullscreen
;
946 @XField!("uint")("color interpolation") bool cinterp
;
947 @XField!("uint")("window border") bool winborder
;
948 @XField!("uint")("show cursor") bool showcursor
;
949 @XField!("int")("scale") int scale
;
950 @XField!("uint")("allow resize") bool allowresize
;
951 @XField!("uint")("always on top") bool alwaysontop
;
952 @XField!("Color")("background color") Color bgcolor
;
953 @XField!("uint")("change resolution") bool changeres
;
954 @XField!("uint")("color depth") uint colordepth
;
955 @XField!("uint")("resolution") uint resolution
;
956 @XField!("uint")("fps") uint fps
;
957 @XField!("uint")("window title buttons") bool wintitle
;
958 @XField!("uint")("vsync") bool vsync
;
959 @XField!("uint")("disable screensaver") bool noscreensaver
;
960 @XField!("uint")("enable F4 fullscreen switch") bool enablef4
;
961 @XField!("uint")("enable F1 game info") bool enablef1
;
962 @XField!("uint")("enable Esc game end") bool enableesc
;
963 @XField!("uint")("enable F5/F6 game save/load") bool enablef5f6
;
964 @XField!("uint")("enable F9 game screenshot") bool enablef9
;
965 @XField!("uint")("close button as Esc") bool closeasesc
;
966 @XField!("uint")("process priority") uint procprio
;
967 @XField!("uint")("vsync") bool pauseonbloor
;
969 @XField!("!")("loading progress bar") uint pbar
;
970 TrueColorImage bgpbar
, fgpbar
;
972 @XField!("!")("show splashscreen") bool showsplash
;
973 //TrueColorImage splashimg;
974 ubyte[] splashimg
; // BMP
976 @XField!("uint")("transparent splashscreen") bool slpashtransparent
;
977 @XField!("uint")("translucent splashscreen alpha") ubyte splashalpha
;
978 @XField!("uint")("scale progress bar image") bool pbarscale
;
979 @XField!("!")("icon data") /*TrueColorImage*/ubyte[] icon
; // windoze .ico
980 @XField!("uint")("display error messages") bool showerrors
;
981 @XField!("uint")("log error messages to 'game_errors.log'") bool logerrors
;
982 @XField!("uint")("abort on error") bool abortonerror
;
983 @XField!("uint")("tread uninited vars as '0'") bool zeroallvars
;
984 @XField!("pstr")("author") string author
;
985 @XField!("pstr")("version") string ver
;
986 @XField!("tdatetime")("lastmod for info") DateTime lastmodnfo
;
988 @XField!("pstr")("information") string information
;
989 @XField!("uint")("major") uint major
;
990 @XField!("uint")("minor") uint minor
;
991 @XField!("uint")("release") uint release
;
992 @XField!("uint")("build") uint build
;
993 @XField!("pstr")("company") string company
;
994 @XField!("pstr")("product") string product
;
995 @XField!("pstr")("copyright") string copyright
;
996 @XField!("pstr")("description") string descritpion
;
997 @XField!("tdatetime")("lastmod for global game settings") DateTime lastmod
;
1002 void read_pbar (VFile fl
) {
1003 pbar
= fl
.readNum
!uint;
1008 if (fl
.readNum
!uint) {
1009 auto sz
= fl
.readNum
!uint;
1010 auto npos
= fl
.tell
+sz
;
1011 //fl.seek(sz, Seek.Cur);
1012 bgpbar
= readImage(fl
);
1016 if (fl
.readNum
!uint) {
1017 auto sz
= fl
.readNum
!uint;
1018 auto npos
= fl
.tell
+sz
;
1019 //fl.seek(sz, Seek.Cur);
1020 fgpbar
= readImage(fl
);
1026 void write_pbar (VFile fl
) {
1027 fl
.writeNum
!uint(pbar
);
1029 if (bgpbar
!is null) {
1030 fl
.writeNum
!uint(bgpbar
.width
*bgpbar
.height
*4+3*4); // data size
1031 fl
.writeImage(bgpbar
);
1033 fl
.writeNum
!uint(0);
1035 if (fgpbar
!is null) {
1036 fl
.writeNum
!uint(fgpbar
.width
*fgpbar
.height
*4+3*4); // data size
1037 fl
.writeImage(fgpbar
);
1039 fl
.writeNum
!uint(0);
1044 void read_showsplash (VFile fl
) {
1045 auto showsplash
= fl
.readNum
!uint;
1049 if (fl
.readNum
!uint) {
1050 auto sz
= fl
.readNum
!uint;
1054 writeln("SPLASH! sz=", sz);
1056 auto zst = wrapZLibStreamRO(fl, VFSZLibMode.ZLib, -1, pos, sz);
1059 ubyte[1024] tmp = void;
1060 auto rd = zst.rawRead(tmp[]);
1061 if (rd.length == 0) break;
1064 auto fo = VFile("zsp00.bin", "w");
1066 fl.rawReadExact(buf[]);
1068 fo.rawWriteExact(buf[]);
1072 auto npos
= fl
.tell
+sz
;
1073 //fl.seek(sz, Seek.Cur);
1074 auto zst
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, -1, fl
.tell
, sz
);
1075 splashimg
.length
= 0;
1077 ubyte[1024] tmp
= void;
1078 auto rd
= zst
.rawRead(tmp
[]);
1079 if (rd
.length
== 0) break;
1087 void write_showsplash (VFile fl
) {
1088 fl
.writeNum
!uint(showsplash ?
1 : 0);
1090 if (splashimg
!is null) {
1091 //fl.writeNum!uint(splashimg.width*splashimg.height*4+3*4); // data size
1092 //fl.writeImage(splashimg);
1093 fl
.writeNum
!uint(0);
1095 fl
.writeNum
!uint(0);
1100 void read_icon (VFile fl
) {
1103 auto sz
= fl
.readNum
!uint;
1104 icon
= new ubyte[](sz
);
1105 fl
.rawReadExact(icon
[]);
1108 void write_icon (VFile fl
) {
1109 fl
.writeNum
!uint(cast(uint)icon
.length
);
1110 if (icon
.length
) fl
.rawWriteExact(icon
[]);
1115 // ////////////////////////////////////////////////////////////////////////// //
1116 final class GMGameInfo
: GMSomething
{
1117 @XField!("Color")("background color") Color bgcolor
;
1118 @XField!("uint")("show help in separate window") bool newwindow
;
1119 @XField!("pstr")("caption") string caption
;
1120 @XField!("int")("position x") int x
;
1121 @XField!("int")("position y") int y
;
1122 @XField!("int")("width") int w
;
1123 @XField!("int")("height") int h
;
1124 @XField!("uint")("show window border") bool windowborder
;
1125 @XField!("uint")("allow window resizing") bool allowresize
;
1126 @XField!("uint")("window on top") bool alwaysontop
;
1127 @XField!("uint")("pause game") bool pause
;
1128 @XField!("tdatetime")("lastmod") DateTime lastmod
;
1129 @XField!("pstr")("help text") string text
;
1135 // ////////////////////////////////////////////////////////////////////////// //
1136 final class GMScript
: GMSomething
{
1141 this (uint aidx
, string aname
, string acode
) { idx
= aidx
; name
= aname
; code
= acode
; }
1145 // ////////////////////////////////////////////////////////////////////////// //
1146 final class GMResTree
: GMSomething
{
1165 Node
[] children
; // for dir
1166 GMSomething obj
; // for others
1169 Node
[Node
.Type
.max
+1] roots
;
1172 this (VFile fl
) { load(fl
); }
1175 void load (VFile fl
) {
1176 static Node
.Type
fg2t() (uint n
) {
1178 case 0: return Node
.Type
.Sprite
;
1179 case 1: return Node
.Type
.Sound
;
1180 case 2: return Node
.Type
.Background
;
1181 case 3: return Node
.Type
.Path
;
1182 case 4: return Node
.Type
.Script
;
1183 case 5: return Node
.Type
.Font
;
1184 case 6: return Node
.Type
.Timeline
;
1185 case 7: return Node
.Type
.Object
;
1186 case 8: return Node
.Type
.Room
;
1187 case 9: return Node
.Type
.GameInfo
;
1188 case 10: return Node
.Type
.GameSettings
;
1189 case 11: return Node
.Type
.Extensions
;
1190 default: assert(0, "invalid tree restype");
1194 static Node
.Type
g2t() (uint n
) {
1196 case 1: return Node
.Type
.Object
;
1197 case 2: return Node
.Type
.Sprite
;
1198 case 3: return Node
.Type
.Sound
;
1199 case 4: return Node
.Type
.Room
;
1200 case 6: return Node
.Type
.Background
;
1201 case 7: return Node
.Type
.Script
;
1202 case 8: return Node
.Type
.Path
;
1203 case 9: return Node
.Type
.Font
;
1204 case 10: return Node
.Type
.GameInfo
;
1205 case 11: return Node
.Type
.GameSettings
;
1206 case 12: return Node
.Type
.Timeline
;
1207 case 13: return Node
.Type
.Extensions
;
1208 default: assert(0, "invalid tree restype");
1212 Node
loadTree (Node parent
, Node
.Type ctp
) {
1213 enum Type
{ Invalid
, Root
, SubDir
, Leaf
}
1216 auto v
= fl
.readNum
!uint; // 0: root; 1: subdir; 2: leaf
1217 if (v
== 0 || v
> Type
.max
) { import std
.conv
: to
; throw new Exception("invalid resource type: "~to
!string(v
)); }
1220 if (type
== Type
.Root
) if (parent
!is null) assert(0, "wtf?!");
1221 Node res
= new Node();
1222 auto grp
= fl
.readNum
!uint;
1224 if (gt
!= ctp
) assert(0, "wtf?!");
1225 auto ipa
= fl
.readNum
!uint();
1226 //if (type == Type.Leaf) writeln("index: ", ipa); else writeln("parent: ", ipa);
1227 res
.type
= (type
!= Type
.Leaf ? Node
.Type
.Dir
: gt
);
1228 res
.name
= fl
.readPStr
;
1229 auto childrenCount
= fl
.readNum
!uint;
1230 if (type
== Type
.Leaf
&& childrenCount
> 0) assert(0, "wtf?!");
1231 foreach (immutable _
; 0..childrenCount
) res
.children
~= loadTree(res
, ctp
);
1235 foreach (immutable int ridx
; 0..12) {
1236 auto tree
= loadTree(null, fg2t(ridx
));
1237 roots
[cast(uint)fg2t(ridx
)] = tree
;
1243 // ////////////////////////////////////////////////////////////////////////// //
1247 GMGameSettings gameInfo
;
1248 GMGameInfo gameHelp
;
1249 string
[string
] constants
;
1250 private GMTrigger
[] triggers
;
1251 private GMSprite
[] sprites
;
1252 private GMBackground
[] backgrounds
;
1253 private GMRoom
[] rooms
;
1254 private GMObject
[] objects
;
1255 private GMScript
[] scripts
;
1257 uint lastInstanceId
;
1260 string
[] extensions
;
1262 private GMObject
[string
] oByNameAA
; // objects by name
1263 private GMRoom
[string
] rByNameAA
; // rooms by name
1264 private GMSprite
[string
] sByNameAA
; // sprites by name
1265 private GMBackground
[string
] bByNameAA
; // backgrounds by name
1266 private GMScript
[string
] scrByNameAA
; // scripts by name
1268 this (VFile fl
) { load(fl
); }
1269 this (const(char)[] fname
) { load(VFile(fname
)); }
1271 pure nothrow @trusted @nogc {
1272 GMObject
objParent (GMObject o
) {
1273 pragma(inline
, true);
1274 return (o
is null ?
null : objByNum(o
.parentobjidx
));
1277 GMObject
objByNum (int num
) { pragma(inline
, true); return (num
>= 0 && num
< objects
.length ? objects
.ptr
[num
] : null); }
1278 GMRoom
roomByNum (int num
) { pragma(inline
, true); return (num
>= 0 && num
< rooms
.length ? rooms
.ptr
[num
] : null); }
1279 GMSprite
sprByNum (int num
) { pragma(inline
, true); return (num
>= 0 && num
< sprites
.length ? sprites
.ptr
[num
] : null); }
1280 GMBackground
bgByNum (int num
) { pragma(inline
, true); return (num
>= 0 && num
< backgrounds
.length ? backgrounds
.ptr
[num
] : null); }
1281 GMScript
scriptByNum (int num
) { pragma(inline
, true); return (num
>= 0 && num
< scripts
.length ? scripts
.ptr
[num
] : null); }
1283 GMObject
objByName (const(char)[] name
) { /*pragma(inline, true);*/ if (auto v
= name
in oByNameAA
) return *v
; else return null; }
1284 GMRoom
roomByName (const(char)[] name
) { /*pragma(inline, true);*/ if (auto v
= name
in rByNameAA
) return *v
; else return null; }
1285 GMSprite
sprByName (const(char)[] name
) { /*pragma(inline, true);*/ if (auto v
= name
in sByNameAA
) return *v
; else return null; }
1286 GMBackground
bgByName (const(char)[] name
) { /*pragma(inline, true);*/ if (auto v
= name
in bByNameAA
) return *v
; else return null; }
1287 GMScript
scriptByName (const(char)[] name
) { /*pragma(inline, true);*/ if (auto v
= name
in scrByNameAA
) return *v
; else return null; }
1291 GMObject
forEachObject (bool delegate (GMObject o
) dg
) { foreach (auto v
; objects
) if (v
!is null && dg(v
)) return v
; return null; }
1292 GMRoom
forEachRoom (bool delegate (GMRoom o
) dg
) { foreach (auto v
; rooms
) if (v
!is null && dg(v
)) return v
; return null; }
1293 GMSprite
forEachSprite (bool delegate (GMSprite o
) dg
) { foreach (auto v
; sprites
) if (v
!is null && dg(v
)) return v
; return null; }
1294 GMBackground
forEachBg (bool delegate (GMBackground o
) dg
) { foreach (auto v
; backgrounds
) if (v
!is null && dg(v
)) return v
; return null; }
1295 GMScript
forEachScript (bool delegate (GMScript o
) dg
) { foreach (auto v
; scripts
) if (v
!is null && dg(v
)) return v
; return null; }
1298 void postProcess () {
1299 forEachObject((v
) { oByNameAA
[v
.name
] = v
; return false; });
1300 forEachRoom((v
) { rByNameAA
[v
.name
] = v
; return false; });
1301 forEachSprite((v
) { sByNameAA
[v
.name
] = v
; return false; });
1302 forEachBg((v
) { bByNameAA
[v
.name
] = v
; return false; });
1303 forEachScript((v
) { scrByNameAA
[v
.name
] = v
; return false; });
1306 void load (VFile fl
) {
1307 auto sign
= fl
.readNum
!uint;
1308 if (sign
!= 1234321) throw new Exception("invalid signature");
1309 auto ver
= fl
.readNum
!uint;
1310 if (ver
< 800) throw new Exception("invalid version");
1311 gameid
= fl
.readNum
!uint;
1312 fl
.rawReadExact(gameguid
[]);
1315 auto xver
= fl
.readNum
!uint;
1316 assert(xver
>= 800);
1317 auto pksize
= fl
.readNum
!uint;
1318 auto npos
= fl
.tell
+pksize
;
1319 scope(exit
) fl
.seek(npos
);
1320 auto zst
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, -1, fl
.tell
, pksize
);
1321 gameInfo
= new GMGameSettings();
1326 auto xver
= fl
.readNum
!uint;
1327 assert(xver
>= 800);
1328 auto count
= fl
.readNum
!uint;
1330 auto pksize
= fl
.readNum
!uint;
1331 auto npos
= fl
.tell
+pksize
;
1332 scope(exit
) fl
.seek(npos
);
1333 auto zst
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, -1, fl
.tell
, pksize
);
1334 foreach (uint idx
; 0..count
) {
1335 if (zst
.readNum
!uint) {
1336 auto tg
= new GMTrigger(idx
, zst
);
1337 assert(triggers
.length
== idx
);
1340 assert(triggers
.length
== idx
);
1345 auto lastmod
= fl
.readDateTime
;
1349 auto xver
= fl
.readNum
!uint;
1350 assert(xver
>= 800);
1351 auto count
= fl
.readNum
!uint;
1352 foreach (immutable idx
; 0..count
) {
1353 auto name
= fl
.readPStr
;
1354 auto value
= fl
.readPStr
;
1355 constants
[name
] = value
;
1357 auto lastmod
= fl
.readDateTime
;
1371 foreach (immutable idx
; 0..ResType
.max
+1) {
1372 auto xver
= fl
.readNum
!uint;
1373 assert(xver
>= 800);
1374 auto count
= fl
.readNum
!uint;
1376 foreach (uint c
; 0..count
) {
1377 auto pksize
= fl
.readNum
!uint;
1378 auto npos
= fl
.tell
+pksize
;
1379 scope(exit
) fl
.seek(npos
);
1380 auto zst
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, -1, fl
.tell
, pksize
);
1381 final switch (cast(ResType
)idx
) {
1382 case ResType
.Sounds
: throw new Exception("sound resources aren't supported");
1383 case ResType
.Sprites
:
1384 if (zst
.readNum
!uint) {
1385 auto spr
= new GMSprite(c
, zst
);
1386 assert(sprites
.length
== c
);
1389 assert(sprites
.length
== c
);
1393 case ResType
.Backrounds
:
1394 if (zst
.readNum
!uint) {
1395 auto bg
= new GMBackground(c
, zst
);
1396 assert(backgrounds
.length
== c
);
1399 assert(backgrounds
.length
== c
);
1400 backgrounds
~= null;
1403 case ResType
.Paths
: throw new Exception("path resources aren't supported");
1404 case ResType
.Scripts
:
1405 if (zst
.readNum
!uint) {
1406 string name
= zst
.readPStr
;
1407 auto lastmod
= zst
.readDateTime
;
1408 auto vv
= zst
.readNum
!uint;
1409 if (vv
!= 400 && (vv
< 800 || vv
> 810)) throw new Exception("invalid script version");
1412 import std
.string
: replace
;
1413 string s
= zst
.readPStr
.replace("\r\n", "\n").replace("\r", "\n");
1415 auto sc
= new GMScript(c
, name
, s
);
1416 assert(scripts
.length
== c
);
1420 assert(scripts
.length
== c
);
1424 case ResType
.Fonts
: throw new Exception("font resources aren't supported");
1425 case ResType
.Timelines
: throw new Exception("timeline resources aren't supported");
1426 case ResType
.Objects
:
1427 if (zst
.readNum
!uint) {
1428 auto obj
= new GMObject(c
, zst
);
1429 assert(objects
.length
== c
);
1432 assert(objects
.length
== c
);
1437 if (zst
.readNum
!uint) {
1438 auto room
= new GMRoom(c
, zst
);
1439 assert(rooms
.length
== c
);
1442 assert(rooms
.length
== c
);
1450 // done with resources
1451 lastInstanceId
= fl
.readNum
!uint;
1452 lastTileId
= fl
.readNum
!uint;
1455 auto xver
= fl
.readNum
!uint;
1456 if (/*xver != 620 &&*/ (xver
< 800 || xver
> 810)) throw new Exception("invalid include files version");
1457 auto count
= fl
.readNum
!uint;
1458 if (count
!= 0) throw new Exception("include files are not supported");
1462 auto xver
= fl
.readNum
!uint;
1463 if (xver
!= 700) throw new Exception("invalid package list version");
1464 auto count
= fl
.readNum
!uint;
1465 while (count
--) extensions
~= fl
.readPStr
;
1469 auto xver
= fl
.readNum
!uint;
1470 if (xver
< 800 || xver
> 810) throw new Exception("invalid game information version");
1471 auto pksize
= fl
.readNum
!uint;
1472 auto npos
= fl
.tell
+pksize
;
1473 scope(exit
) fl
.seek(npos
);
1474 auto zst
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, -1, fl
.tell
, pksize
);
1475 gameHelp
= new GMGameInfo();
1480 auto xver
= fl
.readNum
!uint;
1481 if (xver
!= 500) throw new Exception("invalid library dependency version");
1482 auto count
= fl
.readNum
!uint;
1483 if (count
!= 0) throw new Exception("library dependencies are not supported");
1485 // room execution index
1487 auto xver
= fl
.readNum
!uint;
1488 if (xver
!= 500 && xver
!= 540 && xver
!= 700) throw new Exception("invalid library dependency version");
1489 auto count
= fl
.readNum
!uint;
1490 if (count
> 1024*1024) throw new Exception("room execution sequence too long");
1492 roomseq
= new uint[](count
);
1493 foreach (immutable idx
, ref i
; roomseq
) i
= fl
.readNum
!uint;
1497 tree
= new GMResTree(fl
);