more names and gmk event decoding
[gaemu.git] / ungmk / package.d
blob754824526c961c28839b5242e1373d44527d7ee2
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);
286 foreach (int y; 0..h) {
287 foreach (int x; 0..w) {
288 Color clr;
289 clr.b = fl.readNum!ubyte;
290 clr.g = fl.readNum!ubyte;
291 clr.r = fl.readNum!ubyte;
292 clr.a = fl.readNum!ubyte;
293 img.setPixel(x, y, clr);
297 fl.rawReadExact(img.imageData.bytes[0..w*h*4]);
298 // now swap bytes
299 auto bp = img.imageData.bytes.ptr;
300 foreach (immutable _; 0..w*h) {
301 ubyte t = bp[0];
302 bp[0] = bp[2];
303 bp[2] = t;
304 bp += 4;
306 //while (isz--) fl.readNum!ubyte;
307 if (isz > 0) fl.seek(isz, Seek.Cur);
308 return img;
309 } else {
310 return new TrueColorImage(0, 0);
315 void writeImage (VFile fl, TrueColorImage img) {
316 if (img.width > 32767 || img.height > 32767) throw new Exception("too big image");
317 fl.writeNum!uint(cast(uint)img.width);
318 fl.writeNum!uint(cast(uint)img.height);
319 if (img.width > 0 && img.height > 0) {
320 fl.writeNum!uint(cast(uint)img.width*cast(uint)img.height*4);
321 foreach (int y; 0..img.height) {
322 foreach (int x; 0..img.width) {
323 Color clr = img.getPixel(x, y);
324 fl.writeNum!ubyte(clr.b);
325 fl.writeNum!ubyte(clr.g);
326 fl.writeNum!ubyte(clr.r);
327 fl.writeNum!ubyte(clr.a);
334 // ////////////////////////////////////////////////////////////////////////// //
335 struct XField(string typename) {
336 enum TypeName = typename;
337 string desc;
341 // ////////////////////////////////////////////////////////////////////////// //
342 mixin template GenIO() {
343 private static alias Id(alias T) = T;
345 private static string genRead(T) () {
346 string res;
347 foreach (immutable fname; __traits(derivedMembers, T)) {
348 alias mem = Id!(__traits(getMember, T, fname));
349 foreach (immutable uda; __traits(getAttributes, mem)) {
350 static if (typeof(uda).stringof.length > 6 && typeof(uda).stringof[0..7] == "XField!") {
351 static if (uda.TypeName[0] == '!') {
352 // custom
353 res ~= "read_"~fname~"(fl);\n";
354 } else static if (uda.TypeName == "Color") {
355 res ~= fname~".b = fl.readNum!ubyte;\n";
356 res ~= fname~".g = fl.readNum!ubyte;\n";
357 res ~= fname~".r = fl.readNum!ubyte;\n";
358 res ~= fname~".a = fl.readNum!ubyte;\n";
359 } else static if (uda.TypeName == "pstr") {
360 res ~= fname~" = fl.readPStr;\n";
361 } else static if (uda.TypeName == "tdatetime") {
362 res ~= fname~" = fl.readDateTime;\n";
363 } else static if (typeof(mem).stringof == uda.TypeName) {
364 res ~= fname~" = fl.readNum!"~uda.TypeName~";\n";
365 } else static if (is(typeof(mem) == bool)) {
366 res ~= fname~" = (fl.readNum!"~uda.TypeName~" != 0);\n";
367 } else {
368 res ~= fname~" = cast("~typeof(mem).stringof~")fl.readNum!"~uda.TypeName~";\n";
369 //static assert(0, "wtf for '"~fname~"': uda says '"~uda.TypeName~"', type is '"~typeof(mem).stringof~"'");
374 return res;
377 private static string genWrite(T) () {
378 string res;
379 foreach (immutable fname; __traits(derivedMembers, T)) {
380 alias mem = Id!(__traits(getMember, T, fname));
381 foreach (immutable uda; __traits(getAttributes, mem)) {
382 static if (typeof(uda).stringof.length > 6 && typeof(uda).stringof[0..7] == "XField!") {
383 static if (uda.TypeName[0] == '!') {
384 // custom
385 res ~= "write_"~fname~"(fl);\n";
386 } else static if (uda.TypeName == "Color") {
387 res ~= "fl.writeNum!ubyte("~fname~".b);\n";
388 res ~= "fl.writeNum!ubyte("~fname~".g);\n";
389 res ~= "fl.writeNum!ubyte("~fname~".r);\n";
390 res ~= "fl.writeNum!ubyte("~fname~".a);\n";
391 } else static if (uda.TypeName == "pstr") {
392 res ~= "fl.writePStr("~fname~");\n";
393 } else static if (uda.TypeName == "tdatetime") {
394 res ~= "fl.writeDateTime("~fname~");\n";
395 } else static if (typeof(mem).stringof == uda.TypeName) {
396 res ~= "fl.writeNum!"~uda.TypeName~"("~fname~");\n";
397 } else static if (is(typeof(mem) == bool)) {
398 res ~= "fl.writeNum!"~uda.TypeName~"(cast("~uda.TypeName~")("~fname~" ? 1 : 0));\n";
399 } else {
400 //static assert(0, "wtf for '"~fname~"'!");
401 res ~= "fl.writeNum!"~uda.TypeName~"(cast("~uda.TypeName~")"~fname~");\n";
406 return res;
409 private static string genDump(T) () {
410 string res;
411 foreach (immutable fname; __traits(derivedMembers, T)) {
412 alias mem = Id!(__traits(getMember, T, fname));
413 foreach (immutable uda; __traits(getAttributes, mem)) {
414 static if (uda.TypeName[0] == '!') {
415 // custom
416 } else static if (typeof(uda).stringof.length > 6 && typeof(uda).stringof[0..7] == "XField!") {
417 static if (uda.TypeName == "Color") {
418 res ~= "writefln(\""~uda.desc~": rgba(%s,%s,%s,%s)\", "~fname~".r, "~fname~".g, "~fname~".b, "~fname~".a);\n";
419 } else static if (uda.TypeName == "pstr") {
420 res ~= "writeln(\""~uda.desc~": [\", "~fname~", \"]\");\n";
421 } else static if (uda.TypeName == "tdatetime") {
422 //res ~= "writeln(\""~uda.desc~": \", "~fname~");\n";
423 } else {
424 res ~= "writeln(\""~uda.desc~": \", "~fname~");\n";
429 return res;
432 //pragma(msg, genRead!(typeof(this)));
433 //pragma(msg, genWrite!(typeof(this)));
434 //pragma(msg, genDump!(typeof(this)));
435 mixin("void read (VFile fl) {\n"~genRead!(typeof(this))~"}");
436 mixin("void write (VFile fl) {\n"~genWrite!(typeof(this))~"}");
437 mixin("void dump () {\nimport std.stdio;\n"~genDump!(typeof(this))~"}");
442 struct MyStruct {
443 @XField!uint("this is some flag") bool flag;
444 mixin GenIO;
449 // ////////////////////////////////////////////////////////////////////////// //
450 final class GMSprite {
451 public:
452 enum Shape { Precise, Rectangle, Disk, Diamond }
453 enum BBox { Automatic, Full, Manual }
455 public:
456 uint idx;
457 string name;
458 DateTime lastmod;
459 int xofs, yofs;
460 TrueColorImage[] images;
461 Shape shape;
462 ubyte alphaTolerance;
463 bool separateCollisionMasks;
464 BBox bbox;
465 int bbleft, bbtop, bbright, bbbottom;
467 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
469 void dump () {
470 import std.stdio;
471 writeln("name: [", name, "]");
472 //writeln("lastmod: ", lastmod);
473 writeln("ofs: ", xofs, ", ", yofs);
474 writeln("images: ", images.length);
475 foreach (immutable idx, TrueColorImage img; images) {
476 writeln(" #", idx, "; ", img.width, "x", img.height);
478 writeln("shape: ", shape);
479 writeln("alpha tolerance: ", alphaTolerance);
480 writeln("separate collision masks: ", separateCollisionMasks);
481 writeln("bbox: ", bbox, " (", bbleft, ",", bbtop, ")-(", bbright, ",", bbbottom, ")");
484 private:
485 void load (VFile fl) {
486 name = fl.readPStr;
487 lastmod = fl.readDateTime;
488 auto xver = fl.readNum!uint;
489 //writeln("xver: ", xver);
490 if (xver < 800 || xver > 810) throw new Exception("invalid sprite version");
491 xofs = fl.readNum!int;
492 yofs = fl.readNum!int;
493 auto count = fl.readNum!uint;
494 if (count > 65535) throw new Exception("too many images in sprite sprite");
495 // load images
496 foreach (immutable idx; 0..count) {
497 xver = fl.readNum!uint;
498 if (xver < 800) throw new Exception("invalid sprite image version");
499 images ~= fl.readImage;
502 auto v = fl.readNum!uint;
503 if (v > Shape.max) throw new Exception("invalid sprite shape");
504 shape = cast(Shape)v;
507 auto v = fl.readNum!uint;
508 if (v > alphaTolerance.max) throw new Exception("invalid sprite alpha tolerance");
509 alphaTolerance = cast(ubyte)v;
511 separateCollisionMasks = (fl.readNum!uint != 0);
513 auto v = fl.readNum!uint;
514 if (v > BBox.max) throw new Exception("invalid sprite bounding box type");
515 bbox = cast(BBox)v;
517 bbleft = fl.readNum!int;
518 bbright = fl.readNum!int;
519 bbbottom = fl.readNum!int;
520 bbtop = fl.readNum!int;
525 // ////////////////////////////////////////////////////////////////////////// //
526 final class GMBackground {
527 public:
528 uint idx;
529 string name;
530 DateTime lastmod;
531 bool tileset;
532 int tileWidth, tileHeight;
533 int xofs, yofs;
534 int xsep, ysep;
535 TrueColorImage image;
537 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
539 void dump () {
540 import std.stdio;
541 writeln("name: [", name, "]");
542 //writeln("lastmod: ", lastmod);
543 writeln("tileset: ", tileset, "; ", tileWidth, "x", tileHeight);
544 writeln("ofs: ", xofs, ", ", yofs);
545 writeln("sep: ", xsep, ", ", ysep);
546 writeln("image: ", image.width, "x", image.height);
549 private:
550 void load (VFile fl) {
551 name = fl.readPStr;
552 lastmod = fl.readDateTime;
553 auto xver = fl.readNum!uint;
554 //writeln("xver: ", xver);
555 if (xver != 710) throw new Exception("invalid background version");
556 tileset = (fl.readNum!uint != 0);
557 tileWidth = fl.readNum!int;
558 tileHeight = fl.readNum!int;
559 xofs = fl.readNum!int;
560 yofs = fl.readNum!int;
561 xsep = fl.readNum!int;
562 ysep = fl.readNum!int;
563 xver = fl.readNum!uint;
564 //writeln("xver: ", xver);
565 if (xver < 800 || xver > 810) throw new Exception("invalid background info version");
566 image = fl.readImage;
571 // ////////////////////////////////////////////////////////////////////////// //
572 final class GMAction {
573 public:
574 enum Type { Nothing, Function, Code }
576 enum Kind {
577 act_normal,
578 act_begin,
579 act_end,
580 act_else,
581 act_exit,
582 act_repeat,
583 act_var,
584 act_code,
585 act_placeholder,
586 act_separator,
587 act_label,
590 enum ArgType {
591 t_expr,
592 t_string,
593 t_both,
594 t_boolean,
595 t_menu,
596 t_sprite,
597 t_sound,
598 t_background,
599 t_path,
600 t_script,
601 t_object,
602 t_room,
603 t_font,
604 t_color,
605 t_timeline,
606 t_fonststring,
609 public:
610 uint libid;
611 uint id;
612 Kind kind;
613 bool mayberelative;
614 bool question;
615 bool applied;
616 Type type;
617 string funcname;
618 string codename;
619 uint argused;
620 ArgType[8] argtypes;
621 int applyobj; // object index to apply; -1: self; -2: other
622 uint relative;
623 string[8] argvals;
624 bool negated;
626 public:
627 this (VFile fl) { load(fl); }
629 void dump () {
630 import std.stdio;
631 writeln(" libid: ", libid);
632 writeln(" id: ", id);
633 writeln(" kind: ", kind);
634 writeln(" mayberelative: ", mayberelative);
635 writeln(" question: ", question);
636 writeln(" applied: ", applied);
637 writeln(" type: ", type);
638 writeln(" funcname: [", funcname, "]");
639 writeln(" codename: [", codename, "]");
640 writeln(" argused: ", argused);
641 if (argused > 0) writeln(" argtypes: ", argtypes[0..argused]);
642 writeln(" applyobj: ", applyobj);
643 writeln(" relative: ", relative);
644 if (argused > 0) {
645 writeln(" argvals (", argused, "):");
646 foreach (immutable idx, string s; argvals[0..argused]) {
647 import iv.strex;
648 s = s.detab.outdentAll;
649 writeln(" #", idx, ":");
650 foreach (auto ln; s.byLine) if (ln.length) writeln(" ", ln);
652 //writeln(" argvals: ", argvals[0..argused]);
654 writeln(" negated: ", negated);
657 private:
658 void load (VFile fl) {
659 auto xver = fl.readNum!uint;
660 if (xver != 440) throw new Exception("invalid action version");
661 libid = fl.readNum!uint;
662 id = fl.readNum!uint;
664 auto v = fl.readNum!uint;
665 if (v > Kind.max) throw new Exception("invalid action kind");
666 kind = cast(Kind)v;
668 mayberelative = (fl.readNum!uint != 0);
669 question = (fl.readNum!uint != 0);
670 applied = (fl.readNum!uint != 0);
672 auto v = fl.readNum!uint;
673 if (v > Type.max) throw new Exception("invalid action type");
674 type = cast(Type)v;
676 funcname = fl.readPStr;
677 codename = fl.readPStr;
678 argused = fl.readNum!uint;
679 if (argused > 8) throw new Exception("invalid number of arguments used in action");
680 auto akcount = fl.readNum!uint;
681 if (akcount > 8) { import std.conv : to; throw new Exception("invalid number of argument kinds for action: "~to!string(akcount)); }
682 argtypes[] = ArgType.t_string; //FIXME
683 foreach (immutable idx; 0..akcount) {
684 auto v = fl.readNum!uint;
685 if (v > ArgType.max) throw new Exception("invalid argument type");
686 argtypes[idx] = cast(ArgType)v;
688 applyobj = fl.readNum!int;
689 relative = fl.readNum!uint;
690 auto akvals = fl.readNum!uint;
691 if (akvals != akcount) { import std.conv : to; throw new Exception("invalid number of argument values for action: "~to!string(akvals)); }
692 argvals[] = null;
693 foreach (immutable idx; 0..akvals) argvals[idx] = fl.readPStr;
694 negated = (fl.readNum!uint != 0);
699 // ////////////////////////////////////////////////////////////////////////// //
700 final class GMEvent {
701 public:
702 enum Type {
703 ev_create,
704 ev_destroy,
705 ev_alarm,
706 ev_step,
707 ev_collision,
708 ev_keyboard,
709 ev_mouse,
710 ev_other,
711 ev_draw,
712 ev_keypress,
713 ev_keyrelease,
714 ev_trigger,
717 Type type;
718 uint id;
719 GMAction[] actions;
721 public:
722 this (VFile fl, Type atype, uint aid) { type = atype; load(fl, aid); }
724 void dump () {
725 import std.stdio;
726 writeln("event id=", id, "; type=", evType(type), "; action count: ", actions.length);
727 foreach (immutable idx, GMAction act; actions) {
728 writeln(" -- action #", idx, " --");
729 act.dump;
733 static string evType (Type t) {
734 import std.string : format;
735 return (t <= Type.max ? "%s".format(t) : "<%s>".format(cast(uint)t));
738 private:
739 void load (VFile fl, uint aid) {
740 id = aid;
741 auto xver = fl.readNum!uint;
742 if (xver != 400) throw new Exception("invalid event version");
743 auto count = fl.readNum!uint;
744 if (count > 1024) throw new Exception("too many actions in event");
745 foreach (immutable idx; 0..count) actions ~= new GMAction(fl);
750 // ////////////////////////////////////////////////////////////////////////// //
751 final class GMObject {
752 public:
753 uint idx;
754 string name;
755 DateTime lastmod;
756 int spridx; // -1: none
757 bool solid;
758 bool visible;
759 int depth;
760 bool persistent;
761 int parentobjidx; // -100: none
762 int maskspridx; // -1: none
763 GMEvent[][GMEvent.Type.max+1] events;
765 public:
766 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
768 void dump () {
769 import std.stdio;
770 writeln("name: [", name, "]");
771 //writeln("lastmod: ", lastmod);
772 writeln("spridx:", spridx);
773 writeln("solid: ", solid);
774 writeln("visible: ", visible);
775 writeln("depth: ", depth);
776 writeln("persistent: ", persistent);
777 writeln("parentobjidx: ", parentobjidx);
778 writeln("maskspridx: ", maskspridx);
779 foreach (immutable evidx, GMEvent[] evs; events) {
780 if (evs.length == 0) continue;
781 writeln(" -- evidx: ", evidx, " --");
782 foreach (immutable idx, GMEvent ev; evs) {
783 writeln(" -- event #", idx, " --");
784 ev.dump;
789 private:
790 void load (VFile fl) {
791 name = fl.readPStr;
792 lastmod = fl.readDateTime;
793 auto xver = fl.readNum!uint;
794 if (xver != 430) throw new Exception("invalid object version");
795 spridx = fl.readNum!int;
796 solid = (fl.readNum!uint != 0);
797 visible = (fl.readNum!uint != 0);
798 depth = fl.readNum!int;
799 persistent = (fl.readNum!uint != 0);
800 parentobjidx = fl.readNum!int;
801 maskspridx = fl.readNum!int;
802 auto ecount = fl.readNum!uint;
803 if (ecount != 10 && ecount != 11) throw new Exception("invalid number of event types");
804 foreach (immutable evidx; 0..ecount+1) {
805 GMEvent[] lst;
806 for (;;) {
807 int eid = fl.readNum!int;
808 if (eid == -1) break;
809 lst ~= new GMEvent(fl, cast(GMEvent.Type)evidx, eid);
811 events[evidx] = lst;
817 // ////////////////////////////////////////////////////////////////////////// //
818 final class GMRoom {
819 public:
820 static struct Back {
821 public:
822 bool visibleOnStart;
823 bool fgimage;
824 int bgimageidx; // -1: none
825 int x, y;
826 int xtile, ytile;
827 int xspeed, yspeed;
828 bool stretch;
830 public:
831 void dump () {
832 import std.stdio;
833 writeln("visibleOnStart: ", visibleOnStart);
834 writeln("fgimage: ", fgimage);
835 writeln("bgimageidx: ", bgimageidx);
836 writeln("pos: (", x, ",", y, ")");
837 writeln("tile: (", xtile, ",", ytile, ")");
838 writeln("speed: (", xspeed, ",", yspeed, ")");
839 writeln("stretch: ", stretch);
842 private:
843 void load (VFile fl) {
844 visibleOnStart = (fl.readNum!uint != 0);
845 fgimage = (fl.readNum!uint != 0);
846 bgimageidx = fl.readNum!int;
847 x = fl.readNum!int;
848 y = fl.readNum!int;
849 xtile = fl.readNum!int;
850 ytile = fl.readNum!int;
851 xspeed = fl.readNum!int;
852 yspeed = fl.readNum!int;
853 stretch = (fl.readNum!uint != 0);
857 static struct View {
858 public:
859 bool visibleOnStart;
860 int x, y;
861 int width, height;
862 int portx, porty;
863 int portw, porth;
864 int xborder, yborder;
865 int xspace, yspace;
866 int objfollow; // -1: none
868 public:
869 void dump () {
870 import std.stdio;
871 writeln("visibleOnStart: ", visibleOnStart);
872 writeln("view: (", x, ",", y, "); ", width, "x", height);
873 writeln("port: (", portx, ",", porty, "); ", portw, "x", porth);
874 writeln("border: (", xborder, ",", yborder, ")");
875 writeln("space: (", xspace, ",", yspace, ")");
876 writeln("objfollow: ", objfollow);
879 private:
880 void load (VFile fl, uint xver) {
881 visibleOnStart = (fl.readNum!uint != 0);
882 if (xver == 520) {
883 x = fl.readNum!int;
884 y = fl.readNum!int;
885 width = fl.readNum!int;
886 height = fl.readNum!int;
887 portx = fl.readNum!int;
888 porty = fl.readNum!int;
889 portw = porth = 0;
890 } else {
891 x = fl.readNum!int;
892 y = fl.readNum!int;
893 width = fl.readNum!int;
894 height = fl.readNum!int;
895 portx = fl.readNum!int;
896 porty = fl.readNum!int;
897 portw = fl.readNum!int;
898 porth = fl.readNum!int;
900 xborder = fl.readNum!int;
901 yborder = fl.readNum!int;
902 xspace = fl.readNum!int;
903 yspace = fl.readNum!int;
904 objfollow = fl.readNum!int;
908 static struct Inst {
909 public:
910 int x, y;
911 int objidx;
912 uint id;
913 string createcode;
914 bool locked;
916 public:
917 this (VFile fl) { load(fl); }
919 void dump () {
920 import std.stdio;
921 writeln("position: ", x, "x", y);
922 writeln("objidx: ", objidx);
923 writeln("id: ", id);
924 writeln("createcode: ", createcode.quote);
925 writeln("locked: ", locked);
928 private:
929 void load (VFile fl) {
930 x = fl.readNum!int;
931 y = fl.readNum!int;
932 objidx = fl.readNum!int;
933 id = fl.readNum!uint;
934 createcode = fl.readPStr;
935 locked = (fl.readNum!uint != 0);
939 static struct Tile {
940 public:
941 int x, y;
942 int bgidx;
943 int xtile, ytile;
944 int wtile, htile;
945 int layer;
946 uint id;
947 bool locked;
949 public:
950 this (VFile fl) { load(fl); }
952 void dump () {
953 import std.stdio;
954 writeln("position: ", x, "x", y);
955 writeln("bgidx: ", bgidx);
956 writeln("tile: (", xtile, ",", ytile, "); ", wtile, "x", htile);
957 writeln("layer: ", layer);
958 writeln("id: ", id);
959 writeln("locked: ", locked);
962 private:
963 void load (VFile fl) {
964 x = fl.readNum!int;
965 y = fl.readNum!int;
966 bgidx = fl.readNum!int;
967 xtile = fl.readNum!int;
968 ytile = fl.readNum!int;
969 wtile = fl.readNum!int;
970 htile = fl.readNum!int;
971 layer = fl.readNum!int;
972 id = fl.readNum!uint;
973 locked = (fl.readNum!uint != 0);
977 public:
978 uint idx;
979 string name;
980 string caption;
981 DateTime lastmod;
982 uint width, height;
983 int xsnap, ysnap;
984 bool isogrid;
985 uint speed;
986 bool persistent;
987 uint bgcolor;
988 bool drawbgcolor;
989 string createcode;
990 Back[8] backs;
991 bool viewsEnabled;
992 View[8] views;
993 Inst[] insts;
994 Tile[] tiles;
995 int tilew, tileh;
996 int xtsep, ytsep;
997 int xtofs, ytofs;
999 public:
1000 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
1002 void dump () {
1003 import std.stdio;
1004 writeln("name: [", name, "]");
1005 //writeln("lastmod: ", lastmod);
1006 writeln("caption: [", caption, "]");
1007 writeln("size: ", width, "x", height);
1008 writeln("snap: ", xsnap, "x", ysnap);
1009 writeln("isogrid: ", isogrid);
1010 writeln("speed: ", speed);
1011 writeln("persistent: ", persistent);
1012 writefln("bgcolor: 0x%08x", bgcolor);
1013 writeln("drawbgcolor: ", drawbgcolor);
1014 writeln("tile size: ", tilew, "x", tileh);
1015 writeln("tile sep: ", xtsep, "x", ytsep);
1016 writeln("tile ofs: ", xtofs, "x", ytofs);
1017 writeln("createcode: ", createcode.quote);
1018 foreach (immutable idx, ref Back b; backs) {
1019 if (b.bgimageidx == -1) continue;
1020 writeln("-- background #", idx, " --");
1021 b.dump;
1023 writeln("viewsEnabled: ", viewsEnabled);
1024 foreach (immutable idx, ref View v; views) {
1025 writeln("-- view #", idx, " --");
1026 v.dump;
1028 foreach (immutable idx, ref Inst i; insts) {
1029 writeln("-- instance #", idx, " --");
1030 i.dump;
1032 foreach (immutable idx, ref Tile t; tiles) {
1033 writeln("-- tile #", idx, " --");
1034 t.dump;
1038 private:
1039 void load (VFile fl) {
1040 name = fl.readPStr;
1041 lastmod = fl.readDateTime;
1042 auto xver = fl.readNum!uint;
1043 if (xver != 520 && xver != 541) throw new Exception("invalid room version");
1044 caption = fl.readPStr;
1045 width = fl.readNum!uint;
1046 height = fl.readNum!uint;
1047 xsnap = fl.readNum!int;
1048 ysnap = fl.readNum!int;
1049 isogrid = (fl.readNum!uint != 0);
1050 speed = fl.readNum!int;
1051 persistent = (fl.readNum!uint != 0);
1052 bgcolor = fl.readNum!uint;
1053 drawbgcolor = (fl.readNum!uint != 0);
1054 createcode = fl.readPStr;
1055 if (fl.readNum!uint != 8) throw new Exception("invalid number of backgrounds in room");
1056 foreach (ref Back b; backs) b.load(fl);
1057 viewsEnabled = (fl.readNum!uint != 0);
1058 if (fl.readNum!uint != 8) throw new Exception("invalid number of views in room");
1059 foreach (ref View v; views) v.load(fl, xver);
1060 auto count = fl.readNum!uint;
1061 if (count > 1024*1024) throw new Exception("too many instances in room");
1062 foreach (immutable idx; 0..count) insts ~= Inst(fl);
1063 count = fl.readNum!uint;
1064 if (count > 1024*1024) throw new Exception("too many tiles in room");
1065 foreach (immutable idx; 0..count) tiles ~= Tile(fl);
1066 bool rei = (fl.readNum!uint != 0); // room editor info
1067 // ignore some REI
1068 fl.readNum!uint; // REI width
1069 fl.readNum!uint; // REI height
1070 fl.readNum!uint; // REI grid show bool
1071 fl.readNum!uint; // REI objects show bool
1072 fl.readNum!uint; // REI tiles show bool
1073 fl.readNum!uint; // REI bg show bool
1074 fl.readNum!uint; // REI fg show bool
1075 fl.readNum!uint; // REI views show bool
1076 fl.readNum!uint; // REI delete underlying objects bool
1077 fl.readNum!uint; // REI delete underlying tiles bool
1078 if (xver == 520) {
1079 tilew = fl.readNum!int;
1080 tileh = fl.readNum!int;
1081 xtsep = fl.readNum!int;
1082 ytsep = fl.readNum!int;
1083 xtofs = fl.readNum!int;
1084 ytofs = fl.readNum!int;
1085 } else {
1086 tilew = tileh = 16;
1087 xtsep = ytsep = 1;
1088 xtofs = ytofs = 0;
1090 fl.readNum!uint; // REI tab
1091 fl.readNum!uint; // REI scroll x
1092 fl.readNum!uint; // REI scroll y
1097 // ////////////////////////////////////////////////////////////////////////// //
1098 final class GMTrigger {
1099 public:
1100 enum When { Begin, Middle, End }
1102 public:
1103 uint idx;
1104 string name;
1105 string condition;
1106 When when;
1107 string constname;
1109 public:
1110 this (uint aidx, VFile fl) { idx = aidx; load(fl); }
1112 void dump () {
1113 import std.stdio;
1114 writeln("name: [", name, "]");
1115 writeln("condition: [", condition.quote, "]");
1116 writeln("when: ", when);
1117 writeln("constname: [", constname, "]");
1120 private:
1121 void load (VFile fl) {
1122 auto xver = fl.readNum!uint;
1123 writeln(xver);
1124 if (xver != 800) throw new Exception("invalid trigger version");
1125 name = fl.readPStr;
1126 condition = fl.readPStr;
1128 auto v = fl.readNum!uint;
1129 if (v > When.max) throw new Exception("invalid trigger when");
1130 when = cast(When)v;
1132 constname = fl.readPStr;
1137 // ////////////////////////////////////////////////////////////////////////// //
1138 final class GMGameInfo {
1139 @XField!("uint")("fullscreen") bool fullscreen;
1140 @XField!("uint")("color interpolation") bool cinterp;
1141 @XField!("uint")("window border") bool winborder;
1142 @XField!("uint")("show cursor") bool showcursor;
1143 @XField!("int")("scale") int scale;
1144 @XField!("uint")("allow resize") bool allowresize;
1145 @XField!("uint")("always on top") bool alwaysontop;
1146 @XField!("Color")("background color") Color bgcolor;
1147 @XField!("uint")("change resolution") bool changeres;
1148 @XField!("uint")("color depth") uint colordepth;
1149 @XField!("uint")("resolution") uint resolution;
1150 @XField!("uint")("fps") uint fps;
1151 @XField!("uint")("window title buttons") bool wintitle;
1152 @XField!("uint")("vsync") bool vsync;
1153 @XField!("uint")("disable screensaver") bool noscreensaver;
1154 @XField!("uint")("enable F4 fullscreen switch") bool enablef4;
1155 @XField!("uint")("enable F1 game info") bool enablef1;
1156 @XField!("uint")("enable Esc game end") bool enableesc;
1157 @XField!("uint")("enable F5/F6 game save/load") bool enablef5f6;
1158 @XField!("uint")("enable F9 game screenshot") bool enablef9;
1159 @XField!("uint")("close button as Esc") bool closeasesc;
1160 @XField!("uint")("process priority") uint procprio;
1161 @XField!("uint")("vsync") bool pauseonbloor;
1163 @XField!("!")("loading progress bar") uint pbar;
1164 TrueColorImage bgpbar, fgpbar;
1166 @XField!("!")("show splashscreen") bool showsplash;
1167 //TrueColorImage splashimg;
1168 ubyte[] splashimg; // BMP
1170 @XField!("uint")("transparent splashscreen") bool slpashtransparent;
1171 @XField!("uint")("translucent splashscreen alpha") ubyte splashalpha;
1172 @XField!("uint")("scale progress bar image") bool pbarscale;
1173 @XField!("!")("icon data") /*TrueColorImage*/ubyte[] icon; // windoze .ico
1174 @XField!("uint")("display error messages") bool showerrors;
1175 @XField!("uint")("log error messages to 'game_errors.log'") bool logerrors;
1176 @XField!("uint")("abort on error") bool abortonerror;
1177 @XField!("uint")("tread uninited vars as '0'") bool zeroallvars;
1178 @XField!("pstr")("author") string author;
1179 @XField!("pstr")("version") string ver;
1180 @XField!("tdatetime")("lastmod for info") DateTime lastmodnfo;
1181 // information
1182 @XField!("pstr")("information") string information;
1183 @XField!("uint")("major") uint major;
1184 @XField!("uint")("minor") uint minor;
1185 @XField!("uint")("release") uint release;
1186 @XField!("uint")("build") uint build;
1187 @XField!("pstr")("company") string company;
1188 @XField!("pstr")("product") string product;
1189 @XField!("pstr")("copyright") string copyright;
1190 @XField!("pstr")("description") string descritpion;
1191 @XField!("tdatetime")("lastmod for global game settings") DateTime lastmod;
1193 mixin GenIO;
1195 private:
1196 void read_pbar (VFile fl) {
1197 pbar = fl.readNum!uint;
1198 bgpbar = null;
1199 fgpbar = null;
1200 if (pbar == 2) {
1201 // back image
1202 if (fl.readNum!uint) {
1203 auto sz = fl.readNum!uint;
1204 auto npos = fl.tell+sz;
1205 //fl.seek(sz, Seek.Cur);
1206 bgpbar = readImage(fl);
1207 fl.seek(npos);
1209 // front image
1210 if (fl.readNum!uint) {
1211 auto sz = fl.readNum!uint;
1212 auto npos = fl.tell+sz;
1213 //fl.seek(sz, Seek.Cur);
1214 fgpbar = readImage(fl);
1215 fl.seek(npos);
1220 void write_pbar (VFile fl) {
1221 fl.writeNum!uint(pbar);
1222 if (pbar == 2) {
1223 if (bgpbar !is null) {
1224 fl.writeNum!uint(bgpbar.width*bgpbar.height*4+3*4); // data size
1225 fl.writeImage(bgpbar);
1226 } else {
1227 fl.writeNum!uint(0);
1229 if (fgpbar !is null) {
1230 fl.writeNum!uint(fgpbar.width*fgpbar.height*4+3*4); // data size
1231 fl.writeImage(fgpbar);
1232 } else {
1233 fl.writeNum!uint(0);
1238 void read_showsplash (VFile fl) {
1239 auto showsplash = fl.readNum!uint;
1240 splashimg = null;
1241 if (showsplash) {
1242 // image
1243 if (fl.readNum!uint) {
1244 auto sz = fl.readNum!uint;
1247 import std.stdio;
1248 writeln("SPLASH! sz=", sz);
1249 auto pos = fl.tell;
1250 auto zst = wrapZLibStreamRO(fl, VFSZLibMode.ZLib, -1, pos, sz);
1251 ubyte[] buf;
1252 for (;;) {
1253 ubyte[1024] tmp = void;
1254 auto rd = zst.rawRead(tmp[]);
1255 if (rd.length == 0) break;
1256 buf ~= rd[];
1258 auto fo = VFile("zsp00.bin", "w");
1260 fl.rawReadExact(buf[]);
1262 fo.rawWriteExact(buf[]);
1263 fl.seek(pos);
1266 auto npos = fl.tell+sz;
1267 //fl.seek(sz, Seek.Cur);
1268 auto zst = wrapZLibStreamRO(fl, VFSZLibMode.ZLib, -1, fl.tell, sz);
1269 splashimg.length = 0;
1270 for (;;) {
1271 ubyte[1024] tmp = void;
1272 auto rd = zst.rawRead(tmp[]);
1273 if (rd.length == 0) break;
1274 splashimg ~= rd[];
1276 fl.seek(npos);
1281 void write_showsplash (VFile fl) {
1282 fl.writeNum!uint(showsplash ? 1 : 0);
1283 if (showsplash) {
1284 if (splashimg !is null) {
1285 //fl.writeNum!uint(splashimg.width*splashimg.height*4+3*4); // data size
1286 //fl.writeImage(splashimg);
1287 fl.writeNum!uint(0);
1288 } else {
1289 fl.writeNum!uint(0);
1294 void read_icon (VFile fl) {
1295 icon = null;
1296 // image
1297 auto sz = fl.readNum!uint;
1298 icon = new ubyte[](sz);
1299 fl.rawReadExact(icon[]);
1302 void write_icon (VFile fl) {
1303 fl.writeNum!uint(cast(uint)icon.length);
1304 if (icon.length) fl.rawWriteExact(icon[]);
1309 // ////////////////////////////////////////////////////////////////////////// //
1310 final class GMGameHelp {
1311 @XField!("Color")("background color") Color bgcolor;
1312 @XField!("uint")("show help in separate window") bool newwindow;
1313 @XField!("pstr")("caption") string caption;
1314 @XField!("int")("position x") int x;
1315 @XField!("int")("position y") int y;
1316 @XField!("int")("width") int w;
1317 @XField!("int")("height") int h;
1318 @XField!("uint")("show window border") bool windowborder;
1319 @XField!("uint")("allow window resizing") bool allowresize;
1320 @XField!("uint")("window on top") bool alwaysontop;
1321 @XField!("uint")("pause game") bool pause;
1322 @XField!("tdatetime")("lastmod") DateTime lastmod;
1323 @XField!("pstr")("help text") string text;
1325 mixin GenIO;
1329 // ////////////////////////////////////////////////////////////////////////// //
1330 final class GMScript {
1331 uint idx;
1332 string name;
1333 string code;
1335 this (uint aidx, string aname, string acode) { idx = aidx; name = aname; code = acode; }
1339 // ////////////////////////////////////////////////////////////////////////// //
1340 final class Gmk {
1341 GMGameInfo gameInfo;
1342 GMGameHelp gameHelp;
1343 private GMTrigger[] triggers;
1344 private GMSprite[] sprites;
1345 private GMBackground[] backgrounds;
1346 private GMRoom[] rooms;
1347 private GMObject[] objects;
1348 private GMScript[] scripts;
1350 private GMObject[string] oByNameAA; // objects by name
1351 private GMRoom[string] rByNameAA; // rooms by name
1352 private GMSprite[string] sByNameAA; // sprites by name
1353 private GMBackground[string] bByNameAA; // backgrounds by name
1354 private GMScript[string] scrByNameAA; // scripts by name
1356 this (VFile fl, bool dump=false) { load(fl, dump); }
1357 this (const(char)[] fname, bool dump=false) { load(VFile(fname), dump); }
1359 pure nothrow @trusted @nogc {
1360 GMObject objParent (GMObject o) {
1361 pragma(inline, true);
1362 return (o is null ? null : objByNum(o.parentobjidx));
1365 GMObject objByNum (int num) { pragma(inline, true); return (num >= 0 && num < objects.length ? objects.ptr[num] : null); }
1366 GMRoom roomByNum (int num) { pragma(inline, true); return (num >= 0 && num < rooms.length ? rooms.ptr[num] : null); }
1367 GMSprite sprByNum (int num) { pragma(inline, true); return (num >= 0 && num < sprites.length ? sprites.ptr[num] : null); }
1368 GMBackground bgByNum (int num) { pragma(inline, true); return (num >= 0 && num < backgrounds.length ? backgrounds.ptr[num] : null); }
1369 GMScript scriptByNum (int num) { pragma(inline, true); return (num >= 0 && num < scripts.length ? scripts.ptr[num] : null); }
1371 GMObject objByName (const(char)[] name) { /*pragma(inline, true);*/ if (auto v = name in oByNameAA) return *v; else return null; }
1372 GMRoom roomByName (const(char)[] name) { /*pragma(inline, true);*/ if (auto v = name in rByNameAA) return *v; else return null; }
1373 GMSprite sprByName (const(char)[] name) { /*pragma(inline, true);*/ if (auto v = name in sByNameAA) return *v; else return null; }
1374 GMBackground bgByName (const(char)[] name) { /*pragma(inline, true);*/ if (auto v = name in bByNameAA) return *v; else return null; }
1375 GMScript scriptByName (const(char)[] name) { /*pragma(inline, true);*/ if (auto v = name in scrByNameAA) return *v; else return null; }
1378 // `true` to stop
1379 GMObject forEachObject (bool delegate (GMObject o) dg) { foreach (auto v; objects) if (v !is null && dg(v)) return v; return null; }
1380 GMRoom forEachRoom (bool delegate (GMRoom o) dg) { foreach (auto v; rooms) if (v !is null && dg(v)) return v; return null; }
1381 GMSprite forEachSprite (bool delegate (GMSprite o) dg) { foreach (auto v; sprites) if (v !is null && dg(v)) return v; return null; }
1382 GMBackground forEachBg (bool delegate (GMBackground o) dg) { foreach (auto v; backgrounds) if (v !is null && dg(v)) return v; return null; }
1383 GMScript forEachScript (bool delegate (GMScript o) dg) { foreach (auto v; scripts) if (v !is null && dg(v)) return v; return null; }
1385 private:
1386 void postProcess () {
1387 forEachObject((v) { oByNameAA[v.name] = v; return false; });
1388 forEachRoom((v) { rByNameAA[v.name] = v; return false; });
1389 forEachSprite((v) { sByNameAA[v.name] = v; return false; });
1390 forEachBg((v) { bByNameAA[v.name] = v; return false; });
1391 forEachScript((v) { scrByNameAA[v.name] = v; return false; });
1394 void load (VFile fl, bool dodump) {
1395 auto sign = fl.readNum!uint;
1396 if (sign != 1234321) throw new Exception("invalid signature");
1397 auto ver = fl.readNum!uint;
1398 if (dodump) writeln("version: ", ver/100, ".", ver%100);
1399 if (ver < 800) throw new Exception("invalid version");
1400 auto gameid = fl.readNum!uint;
1401 if (dodump) writeln("game id: ", gameid);
1402 ubyte[16] gameguid = void;
1403 fl.rawReadExact(gameguid[]);
1404 if (dodump) { write("game id: "); foreach (ubyte b; gameguid[]) writef("%02x", b); writeln; }
1405 // game settings
1407 if (dodump) writeln("=== game settings ===");
1408 auto xver = fl.readNum!uint;
1409 if (dodump) writeln("xver: ", xver);
1410 assert(xver >= 800);
1411 auto pksize = fl.readNum!uint;
1412 auto npos = fl.tell+pksize;
1413 scope(exit) fl.seek(npos);
1414 //writeln("size: ", pksize);
1415 auto zst = wrapZLibStreamRO(fl, VFSZLibMode.ZLib, -1, fl.tell, pksize);
1416 //writeln("unpacked size: ", zst.size);
1417 auto gi = new GMGameInfo();
1418 gi.read(zst);
1419 if (dodump) gi.dump;
1420 gameInfo = gi;
1422 // triggers
1424 if (dodump) writeln("=== triggers ===");
1425 auto xver = fl.readNum!uint;
1426 if (dodump) writeln("xver: ", xver);
1427 assert(xver >= 800);
1428 auto count = fl.readNum!uint;
1429 if (dodump) writeln("count: ", count);
1430 if (count > 0) {
1431 auto pksize = fl.readNum!uint;
1432 auto npos = fl.tell+pksize;
1433 scope(exit) fl.seek(npos);
1434 if (dodump) writeln("pksize: ", pksize);
1435 auto zst = wrapZLibStreamRO(fl, VFSZLibMode.ZLib, -1, fl.tell, pksize);
1436 if (dodump) writeln("upsize: ", zst.size);
1437 foreach (uint idx; 0..count) {
1438 if (zst.readNum!uint) {
1439 auto tg = new GMTrigger(idx, zst);
1440 if (dodump) writeln("-- trigger #", idx, " --");
1441 if (dodump) tg.dump;
1442 assert(triggers.length == idx);
1443 triggers ~= tg;
1444 } else {
1445 assert(triggers.length == idx);
1446 triggers ~= null;
1450 auto lastmod = fl.readDateTime;
1451 //if (dodump) writeln("lastmod: ", lastmod);
1453 // constants
1455 if (dodump) writeln("=== constants ===");
1456 auto xver = fl.readNum!uint;
1457 if (dodump) writeln("xver: ", xver);
1458 assert(xver >= 800);
1459 auto count = fl.readNum!uint;
1460 if (dodump) writeln("count: ", count);
1461 foreach (immutable idx; 0..count) {
1462 auto name = fl.readPStr;
1463 auto value = fl.readPStr;
1464 if (dodump) writeln("[", name, "]=[", value, "]");
1466 auto lastmod = fl.readDateTime;
1467 //if (dodump) writeln("lastmod: ", lastmod);
1469 // resources
1470 static immutable string[9] ResNames = [
1471 "Sounds",
1472 "Sprites",
1473 "Backrounds",
1474 "Paths",
1475 "Scripts",
1476 "Fonts",
1477 "Timelines",
1478 "Objects",
1479 "Rooms",
1481 enum ResType {
1482 Sounds,
1483 Sprites,
1484 Backrounds,
1485 Paths,
1486 Scripts,
1487 Fonts,
1488 Timelines,
1489 Objects,
1490 Rooms,
1492 foreach (immutable idx, string resname; ResNames) {
1493 if (dodump) writeln("=== res:", resname, " ===");
1494 auto xver = fl.readNum!uint;
1495 if (dodump) writeln("xver: ", ver);
1496 assert(xver >= 800);
1497 auto count = fl.readNum!uint;
1498 if (dodump) writeln("count: ", count);
1499 if (count > 0) {
1500 foreach (uint c; 0..count) {
1501 auto pksize = fl.readNum!uint;
1502 auto npos = fl.tell+pksize;
1503 scope(exit) fl.seek(npos);
1504 //writeln("zstream size: ", pksize);
1505 auto zst = wrapZLibStreamRO(fl, VFSZLibMode.ZLib, -1, fl.tell, pksize);
1506 if (idx == ResType.Sprites) {
1507 // sprites
1508 if (zst.readNum!uint) {
1509 auto spr = new GMSprite(c, zst);
1510 if (dodump) writeln("-- sprite #", c, " --");
1511 if (dodump) spr.dump;
1512 assert(sprites.length == c);
1513 sprites ~= spr;
1514 } else {
1515 if (dodump) writeln("-- sprite #", c, " --");
1516 if (dodump) writeln(" NONE");
1517 assert(sprites.length == c);
1518 sprites ~= null;
1520 } else if (idx == ResType.Backrounds) {
1521 // backgrounds
1522 if (zst.readNum!uint) {
1523 auto bg = new GMBackground(c, zst);
1524 if (dodump) writeln("-- background #", c, " --");
1525 if (dodump) bg.dump;
1526 assert(backgrounds.length == c);
1527 backgrounds ~= bg;
1528 } else {
1529 if (dodump) writeln("-- background #", c, " --");
1530 if (dodump) writeln(" NONE");
1531 assert(backgrounds.length == c);
1532 backgrounds ~= null;
1534 } else if (idx == ResType.Scripts) {
1535 // scripts
1536 if (zst.readNum!uint) {
1537 string name = zst.readPStr;
1538 auto lastmod = zst.readDateTime;
1539 if (dodump) writeln("-- script #", c, " --");
1540 if (dodump) writeln("name: [", name, "]");
1541 //if (dodump) writeln("lastmod: ", lastmod);
1542 auto vv = zst.readNum!uint;
1543 if (vv != 400 && (vv < 800 || vv > 810)) throw new Exception("invalid script version");
1544 //writeln("===========\n", zst.readPStr, "\n===========");
1546 import iv.strex;
1547 import std.string : replace;
1548 string s = zst.readPStr.replace("\r\n", "\n").replace("\r", "\n");
1549 s = s.outdentAll;
1550 auto sc = new GMScript(c, name, s);
1551 if (dodump) {
1552 writeln("===========");
1553 foreach (auto ln; s.byLine) if (ln.length) writeln(" ", ln);
1554 writeln("===========");
1556 assert(scripts.length == c);
1557 scripts ~= sc;
1559 } else {
1560 if (dodump) writeln("-- script #", c, " --");
1561 if (dodump) writeln(" NONE");
1562 assert(scripts.length == c);
1563 scripts ~= null;
1565 } else if (idx == ResType.Objects) {
1566 // objects
1567 if (zst.readNum!uint) {
1568 if (dodump) writeln("-- object #", c, " --");
1569 auto obj = new GMObject(c, zst);
1570 if (dodump) obj.dump;
1571 assert(objects.length == c);
1572 objects ~= obj;
1573 } else {
1574 //writeln("-- object #", c, " --");
1575 //writeln(" NONE");
1576 assert(objects.length == c);
1577 objects ~= null;
1579 } else if (idx == ResType.Rooms) {
1580 // rooms
1581 if (zst.readNum!uint) {
1582 if (dodump) writeln("-- room #", c, " --");
1583 auto room = new GMRoom(c, zst);
1584 if (dodump) room.dump;
1585 assert(rooms.length == c);
1586 rooms ~= room;
1587 } else {
1588 //writeln("-- room #", c, " --");
1589 //writeln(" NONE");
1590 assert(rooms.length == c);
1591 rooms ~= null;
1597 // done with resources
1598 auto lastInstanceId = fl.readNum!uint;
1599 auto lastTileId = fl.readNum!uint;
1600 if (dodump) writeln("last instance placed id: ", lastInstanceId);
1601 if (dodump) writeln("last tile placed id: ", lastTileId);
1602 // includes
1604 auto xver = fl.readNum!uint;
1605 if (/*xver != 620 &&*/ (xver < 800 || xver > 810)) throw new Exception("invalid include files version");
1606 auto count = fl.readNum!uint;
1607 if (dodump) writeln("number of include files: ", count);
1608 while (count--) {
1609 auto pksize = fl.readNum!uint;
1610 fl.seek(pksize, Seek.Cur);
1613 // packages
1615 auto xver = fl.readNum!uint;
1616 if (xver != 700) throw new Exception("invalid package list version");
1617 auto count = fl.readNum!uint;
1618 if (dodump) writeln("number of packages files: ", count);
1619 while (count--) {
1620 string name = fl.readPStr;
1621 if (dodump) writeln(" package: [", name, "]");
1624 // game information
1626 auto xver = fl.readNum!uint;
1627 if (xver < 800 || xver > 810) throw new Exception("invalid game information version");
1628 auto pksize = fl.readNum!uint;
1629 auto npos = fl.tell+pksize;
1630 scope(exit) fl.seek(npos);
1631 auto zst = wrapZLibStreamRO(fl, VFSZLibMode.ZLib, -1, fl.tell, pksize);
1632 gameHelp = new GMGameHelp();
1633 gameHelp.read(zst);
1634 if (dodump) gameHelp.dump;
1636 // libdeps
1638 auto xver = fl.readNum!uint;
1639 if (xver != 500) throw new Exception("invalid library dependency version");
1640 auto count = fl.readNum!uint;
1641 while (count--) {
1642 auto lcc = fl.readPStr;
1643 if (dodump) if (lcc.length) writeln("libcreatecode: [", lcc.quote, "]");
1646 // room execution index
1648 auto xver = fl.readNum!uint;
1649 if (xver != 500 && xver != 540 && xver != 700) throw new Exception("invalid library dependency version");
1650 auto count = fl.readNum!uint;
1651 if (count > 1024*1024) throw new Exception("room execution sequence too long");
1652 if (count) {
1653 auto sq = new uint[](count);
1654 foreach (immutable idx, ref i; sq) i = fl.readNum!uint;
1655 if (dodump) writeln("room sequence: ", sq);
1658 // resource tree
1660 static immutable string[14] GroupName = [
1661 "unknown",
1662 "Objects",
1663 "Sprites",
1664 "Sounds",
1665 "Rooms",
1666 "Five",
1667 "Backgrounds",
1668 "Scripts",
1669 "Paths",
1670 "Fonts",
1671 "Game Information",
1672 "Global Game Settings",
1673 "Time Lines",
1674 "Extension Packages",
1677 static immutable string[12] FileGroupName = [
1678 "Sprites",
1679 "Sounds",
1680 "Backgrounds",
1681 "Paths",
1682 "Scripts",
1683 "Fonts",
1684 "Time Lines",
1685 "Objects",
1686 "Rooms",
1687 "Game Information",
1688 "Global Game Settings",
1689 "Extension Packages"
1692 static void writeIndent (int indent) { foreach (immutable n; 0..indent) write(' '); }
1694 static void dumpTree (VFile fl, int ind, int curgrp=-1) {
1695 enum Type { Invalid, Root, SubDir, Leaf }
1696 Type type;
1698 auto v = fl.readNum!uint; // 0: root; 1: subdir; 2: leaf
1699 if (v == 0 || v > Type.max) { import std.conv : to; throw new Exception("invalid resource type: "~to!string(v)); }
1700 type = cast(Type)v;
1702 if (curgrp >= 0) {
1703 writeIndent(ind); writeln("---- [", FileGroupName[curgrp], "] ---");
1704 } else {
1705 writeIndent(ind); writeln("----");
1707 ++ind;
1708 writeIndent(ind); writeln("type: ", type);
1709 auto grp = fl.readNum!uint;
1710 if (grp < GroupName.length) {
1711 writeIndent(ind); writeln("grouping: ", grp, " <", GroupName[grp], ">");
1712 } else {
1713 writeIndent(ind); writeln("grouping: ", grp);
1715 if (type == Type.Leaf) {
1716 writeIndent(ind); writeln("index: ", fl.readNum!uint);
1717 } else {
1718 writeIndent(ind); writeln("parent: ", fl.readNum!uint);
1720 writeIndent(ind); writeln("name: [", fl.readPStr, "]");
1721 auto childrenCount = fl.readNum!uint;
1722 writeIndent(ind); writeln("childrenCount: ", childrenCount);
1723 ++ind;
1724 foreach (immutable _; 0..childrenCount) dumpTree(fl, ind);
1727 //if (dodump) foreach (immutable ridx; 0..12) dumpTree(fl, 0, cast(int)ridx);
1729 postProcess();