better gmk loader
[gaemu.git] / ungmk / package.d
blob90948860a28bd6c86da84edee6375b61dd32b1f5
1 module ungmk is aliced;
3 import std.datetime;
4 import std.stdio;
6 import iv.vfs;
7 import iv.strex;
9 import arsd.color;
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 () {
20 // keys
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";
49 evKeyNames[48] = "0";
50 evKeyNames[49] = "1";
51 evKeyNames[50] = "2";
52 evKeyNames[51] = "3";
53 evKeyNames[52] = "4";
54 evKeyNames[53] = "5";
55 evKeyNames[54] = "6";
56 evKeyNames[55] = "7";
57 evKeyNames[56] = "8";
58 evKeyNames[57] = "9";
60 evKeyNames[65] = "A";
61 evKeyNames[66] = "B";
62 evKeyNames[67] = "C";
63 evKeyNames[68] = "D";
64 evKeyNames[69] = "E";
65 evKeyNames[70] = "F";
66 evKeyNames[71] = "G";
67 evKeyNames[72] = "H";
68 evKeyNames[73] = "I";
69 evKeyNames[74] = "J";
70 evKeyNames[75] = "K";
71 evKeyNames[76] = "L";
72 evKeyNames[77] = "M";
73 evKeyNames[78] = "N";
74 evKeyNames[79] = "O";
75 evKeyNames[80] = "P";
76 evKeyNames[81] = "Q";
77 evKeyNames[82] = "R";
78 evKeyNames[83] = "S";
79 evKeyNames[84] = "T";
80 evKeyNames[85] = "U";
81 evKeyNames[86] = "V";
82 evKeyNames[87] = "W";
83 evKeyNames[88] = "X";
84 evKeyNames[89] = "Y";
85 evKeyNames[90] = "Z";
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";
112 // mouse
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";
161 // other
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) {
214 import std.datetime;
215 import core.stdc.math : modf;
216 auto dt = Date(Date(1899, 12, 30).dayOfGregorianCal+cast(int)d);
217 double i, f;
218 f = modf(d, &i);
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);
229 return d;
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[]);
251 // delphi TDateTime
252 DateTime readDateTime (VFile fl) {
253 auto d = fl.readNum!double;
254 version(none) {
255 auto dt = fromDelphiDT(d);
256 auto dd = toDelphiDT(dt);
257 auto xd = fromDelphiDT(dd);
258 writeln("dt: ", dt);
259 writeln("xd: ", xd);
260 assert(dt == xd);
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");
283 isz -= w*h*4;
284 auto img = new TrueColorImage(w, h);
285 fl.rawReadExact(img.imageData.bytes[0..w*h*4]);
286 // now swap bytes
287 auto bp = img.imageData.bytes.ptr;
288 foreach (immutable _; 0..w*h) {
289 ubyte t = bp[0];
290 bp[0] = bp[2];
291 bp[2] = t;
292 bp += 4;
294 //while (isz--) fl.readNum!ubyte;
295 if (isz > 0) fl.seek(isz, Seek.Cur);
296 return img;
297 } else {
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;
325 string desc;
329 // ////////////////////////////////////////////////////////////////////////// //
330 mixin template GenIO() {
331 private static alias Id(alias T) = T;
333 private static string genRead(T) () {
334 string res;
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] == '!') {
340 // custom
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";
355 } else {
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~"'");
362 return res;
365 private static string genWrite(T) () {
366 string res;
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] == '!') {
372 // custom
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";
387 } else {
388 //static assert(0, "wtf for '"~fname~"'!");
389 res ~= "fl.writeNum!"~uda.TypeName~"(cast("~uda.TypeName~")"~fname~");\n";
394 return res;
398 private static string genDump(T) () {
399 string res;
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] == '!') {
404 // custom
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";
412 } else {
413 res ~= "writeln(\""~uda.desc~": \", "~fname~");\n";
418 return res;
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 {
437 public:
438 enum Shape { Precise, Rectangle, Disk, Diamond }
439 enum BBox { Automatic, Full, Manual }
441 public:
442 uint idx;
443 string name;
444 DateTime lastmod;
445 int xofs, yofs;
446 TrueColorImage[] images;
447 Shape shape;
448 ubyte alphaTolerance;
449 bool separateCollisionMasks;
450 BBox bbox;
451 int bbleft, bbtop, bbright, bbbottom;
453 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
455 private:
456 void load (VFile fl) {
457 name = fl.readPStr;
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");
466 // load images
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");
486 bbox = cast(BBox)v;
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 {
498 public:
499 uint idx;
500 string name;
501 DateTime lastmod;
502 bool tileset;
503 int tileWidth, tileHeight;
504 int xofs, yofs;
505 int xsep, ysep;
506 TrueColorImage image;
508 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
510 private:
511 void load (VFile fl) {
512 name = fl.readPStr;
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 {
534 public:
535 enum Type { Nothing, Function, Code }
537 enum Kind {
538 act_normal,
539 act_begin,
540 act_end,
541 act_else,
542 act_exit,
543 act_repeat,
544 act_var,
545 act_code,
546 act_placeholder,
547 act_separator,
548 act_label,
551 enum ArgType {
552 t_expr,
553 t_string,
554 t_both,
555 t_boolean,
556 t_menu,
557 t_sprite,
558 t_sound,
559 t_background,
560 t_path,
561 t_script,
562 t_object,
563 t_room,
564 t_font,
565 t_color,
566 t_timeline,
567 t_fonststring,
570 public:
571 uint libid;
572 uint id;
573 Kind kind;
574 bool mayberelative;
575 bool question;
576 bool applied;
577 Type type;
578 string funcname;
579 string codename;
580 uint argused;
581 ArgType[8] argtypes;
582 int applyobj; // object index to apply; -1: self; -2: other
583 uint relative;
584 string[8] argvals;
585 bool negated;
587 public:
588 this (VFile fl) { load(fl); }
590 private:
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");
599 kind = cast(Kind)v;
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");
607 type = cast(Type)v;
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)); }
625 argvals[] = null;
626 foreach (immutable idx; 0..akvals) argvals[idx] = fl.readPStr;
627 negated = (fl.readNum!uint != 0);
632 // ////////////////////////////////////////////////////////////////////////// //
633 final class GMEvent : GMSomething {
634 public:
635 enum Type {
636 ev_create,
637 ev_destroy,
638 ev_alarm,
639 ev_step,
640 ev_collision,
641 ev_keyboard,
642 ev_mouse,
643 ev_other,
644 ev_draw,
645 ev_keypress,
646 ev_keyrelease,
647 ev_trigger,
650 Type type;
651 uint id;
652 GMAction[] actions;
654 public:
655 this (VFile fl, Type atype, uint aid) { type = atype; load(fl, aid); }
657 private:
658 void load (VFile fl, uint aid) {
659 id = 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 {
671 public:
672 uint idx;
673 string name;
674 DateTime lastmod;
675 int spridx; // -1: none
676 bool solid;
677 bool visible;
678 int depth;
679 bool persistent;
680 int parentobjidx; // -100: none
681 int maskspridx; // -1: none
682 GMEvent[][GMEvent.Type.max+1] events;
684 public:
685 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
687 private:
688 void load (VFile fl) {
689 name = fl.readPStr;
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) {
703 GMEvent[] lst;
704 for (;;) {
705 int eid = fl.readNum!int;
706 if (eid == -1) break;
707 lst ~= new GMEvent(fl, cast(GMEvent.Type)evidx, eid);
709 events[evidx] = lst;
715 // ////////////////////////////////////////////////////////////////////////// //
716 final class GMRoom : GMSomething {
717 public:
718 static struct Back {
719 public:
720 bool visibleOnStart;
721 bool fgimage;
722 int bgimageidx; // -1: none
723 int x, y;
724 int xtile, ytile;
725 int xspeed, yspeed;
726 bool stretch;
728 private:
729 void load (VFile fl) {
730 visibleOnStart = (fl.readNum!uint != 0);
731 fgimage = (fl.readNum!uint != 0);
732 bgimageidx = fl.readNum!int;
733 x = fl.readNum!int;
734 y = 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);
743 static struct View {
744 public:
745 bool visibleOnStart;
746 int x, y;
747 int width, height;
748 int portx, porty;
749 int portw, porth;
750 int xborder, yborder;
751 int xspace, yspace;
752 int objfollow; // -1: none
754 private:
755 void load (VFile fl, uint xver) {
756 visibleOnStart = (fl.readNum!uint != 0);
757 if (xver == 520) {
758 x = fl.readNum!int;
759 y = fl.readNum!int;
760 width = fl.readNum!int;
761 height = fl.readNum!int;
762 portx = fl.readNum!int;
763 porty = fl.readNum!int;
764 portw = porth = 0;
765 } else {
766 x = fl.readNum!int;
767 y = 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 {
784 public:
785 int x, y;
786 int objidx;
787 uint id;
788 string createcode;
789 bool locked;
791 private:
792 void load (VFile fl) {
793 x = fl.readNum!int;
794 y = fl.readNum!int;
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 {
803 public:
804 int x, y;
805 int bgidx;
806 int xtile, ytile;
807 int wtile, htile;
808 int layer;
809 uint id;
810 bool locked;
812 private:
813 void load (VFile fl) {
814 x = fl.readNum!int;
815 y = fl.readNum!int;
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);
827 public:
828 uint idx;
829 string name;
830 string caption;
831 DateTime lastmod;
832 uint width, height;
833 int xsnap, ysnap;
834 bool isogrid;
835 uint speed;
836 bool persistent;
837 uint bgcolor;
838 bool drawbgcolor;
839 string createcode;
840 Back[8] backs;
841 bool viewsEnabled;
842 View[8] views;
843 Inst[] insts;
844 Tile[] tiles;
845 int tilew, tileh;
846 int xtsep, ytsep;
847 int xtofs, ytofs;
849 public:
850 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
852 private:
853 void load (VFile fl) {
854 name = fl.readPStr;
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
881 // ignore some REI
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
892 if (xver == 520) {
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;
899 } else {
900 tilew = tileh = 16;
901 xtsep = ytsep = 1;
902 xtofs = ytofs = 0;
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 {
913 public:
914 enum When { Begin, Middle, End }
916 public:
917 uint idx;
918 string name;
919 string condition;
920 When when;
921 string constname;
923 public:
924 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
926 private:
927 void load (VFile fl) {
928 auto xver = fl.readNum!uint;
929 writeln(xver);
930 if (xver != 800) throw new Exception("invalid trigger version");
931 name = fl.readPStr;
932 condition = fl.readPStr;
934 auto v = fl.readNum!uint;
935 if (v > When.max) throw new Exception("invalid trigger when");
936 when = cast(When)v;
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;
987 // information
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;
999 mixin GenIO;
1001 private:
1002 void read_pbar (VFile fl) {
1003 pbar = fl.readNum!uint;
1004 bgpbar = null;
1005 fgpbar = null;
1006 if (pbar == 2) {
1007 // back image
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);
1013 fl.seek(npos);
1015 // front image
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);
1021 fl.seek(npos);
1026 void write_pbar (VFile fl) {
1027 fl.writeNum!uint(pbar);
1028 if (pbar == 2) {
1029 if (bgpbar !is null) {
1030 fl.writeNum!uint(bgpbar.width*bgpbar.height*4+3*4); // data size
1031 fl.writeImage(bgpbar);
1032 } else {
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);
1038 } else {
1039 fl.writeNum!uint(0);
1044 void read_showsplash (VFile fl) {
1045 auto showsplash = fl.readNum!uint;
1046 splashimg = null;
1047 if (showsplash) {
1048 // image
1049 if (fl.readNum!uint) {
1050 auto sz = fl.readNum!uint;
1053 import std.stdio;
1054 writeln("SPLASH! sz=", sz);
1055 auto pos = fl.tell;
1056 auto zst = wrapZLibStreamRO(fl, VFSZLibMode.ZLib, -1, pos, sz);
1057 ubyte[] buf;
1058 for (;;) {
1059 ubyte[1024] tmp = void;
1060 auto rd = zst.rawRead(tmp[]);
1061 if (rd.length == 0) break;
1062 buf ~= rd[];
1064 auto fo = VFile("zsp00.bin", "w");
1066 fl.rawReadExact(buf[]);
1068 fo.rawWriteExact(buf[]);
1069 fl.seek(pos);
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;
1076 for (;;) {
1077 ubyte[1024] tmp = void;
1078 auto rd = zst.rawRead(tmp[]);
1079 if (rd.length == 0) break;
1080 splashimg ~= rd[];
1082 fl.seek(npos);
1087 void write_showsplash (VFile fl) {
1088 fl.writeNum!uint(showsplash ? 1 : 0);
1089 if (showsplash) {
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);
1094 } else {
1095 fl.writeNum!uint(0);
1100 void read_icon (VFile fl) {
1101 icon = null;
1102 // image
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;
1131 mixin GenIO;
1135 // ////////////////////////////////////////////////////////////////////////// //
1136 final class GMScript : GMSomething {
1137 uint idx;
1138 string name;
1139 string code;
1141 this (uint aidx, string aname, string acode) { idx = aidx; name = aname; code = acode; }
1145 // ////////////////////////////////////////////////////////////////////////// //
1146 final class GMResTree : GMSomething {
1147 static class Node {
1148 enum Type {
1149 Dir,
1150 Object,
1151 Sprite,
1152 Sound,
1153 Room,
1154 Background,
1155 Script,
1156 Path,
1157 Font,
1158 GameInfo,
1159 GameSettings,
1160 Timeline,
1161 Extensions,
1163 Type type;
1164 string name;
1165 Node[] children; // for dir
1166 GMSomething obj; // for others
1169 Node[Node.Type.max+1] roots;
1171 public:
1172 this (VFile fl) { load(fl); }
1174 private:
1175 void load (VFile fl) {
1176 static Node.Type fg2t() (uint n) {
1177 switch (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) {
1195 switch (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 }
1214 Type type;
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)); }
1218 type = cast(Type)v;
1220 if (type == Type.Root) if (parent !is null) assert(0, "wtf?!");
1221 Node res = new Node();
1222 auto grp = fl.readNum!uint;
1223 auto gt = g2t(grp);
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);
1232 return res;
1235 foreach (immutable int ridx; 0..12) {
1236 auto tree = loadTree(null, fg2t(ridx));
1237 roots[cast(uint)fg2t(ridx)] = tree;
1243 // ////////////////////////////////////////////////////////////////////////// //
1244 final class Gmk {
1245 uint gameid;
1246 ubyte[16] gameguid;
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;
1256 GMResTree tree;
1257 uint lastInstanceId;
1258 uint lastTileId;
1259 uint[] roomseq;
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; }
1290 // `true` to stop
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; }
1297 private:
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[]);
1313 // game settings
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();
1322 gameInfo.read(zst);
1324 // triggers
1326 auto xver = fl.readNum!uint;
1327 assert(xver >= 800);
1328 auto count = fl.readNum!uint;
1329 if (count > 0) {
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);
1338 triggers ~= tg;
1339 } else {
1340 assert(triggers.length == idx);
1341 triggers ~= null;
1345 auto lastmod = fl.readDateTime;
1347 // constants
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;
1359 // resources
1360 enum ResType {
1361 Sounds,
1362 Sprites,
1363 Backrounds,
1364 Paths,
1365 Scripts,
1366 Fonts,
1367 Timelines,
1368 Objects,
1369 Rooms,
1371 foreach (immutable idx; 0..ResType.max+1) {
1372 auto xver = fl.readNum!uint;
1373 assert(xver >= 800);
1374 auto count = fl.readNum!uint;
1375 if (count > 0) {
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);
1387 sprites ~= spr;
1388 } else {
1389 assert(sprites.length == c);
1390 sprites ~= null;
1392 break;
1393 case ResType.Backrounds:
1394 if (zst.readNum!uint) {
1395 auto bg = new GMBackground(c, zst);
1396 assert(backgrounds.length == c);
1397 backgrounds ~= bg;
1398 } else {
1399 assert(backgrounds.length == c);
1400 backgrounds ~= null;
1402 break;
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");
1411 import iv.strex;
1412 import std.string : replace;
1413 string s = zst.readPStr.replace("\r\n", "\n").replace("\r", "\n");
1414 s = s.outdentAll;
1415 auto sc = new GMScript(c, name, s);
1416 assert(scripts.length == c);
1417 scripts ~= sc;
1419 } else {
1420 assert(scripts.length == c);
1421 scripts ~= null;
1423 break;
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);
1430 objects ~= obj;
1431 } else {
1432 assert(objects.length == c);
1433 objects ~= null;
1435 break;
1436 case ResType.Rooms:
1437 if (zst.readNum!uint) {
1438 auto room = new GMRoom(c, zst);
1439 assert(rooms.length == c);
1440 rooms ~= room;
1441 } else {
1442 assert(rooms.length == c);
1443 rooms ~= null;
1445 break;
1450 // done with resources
1451 lastInstanceId = fl.readNum!uint;
1452 lastTileId = fl.readNum!uint;
1453 // includes
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");
1460 // packages
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;
1467 // game information
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();
1476 gameHelp.read(zst);
1478 // libdeps
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");
1491 if (count) {
1492 roomseq = new uint[](count);
1493 foreach (immutable idx, ref i; roomseq) i = fl.readNum!uint;
1496 // resource tree
1497 tree = new GMResTree(fl);
1498 postProcess();