DEADSOFTWARE

map: use streams for map loader
[flatwaifu.git] / src / map.c
1 /* Copyright (C) 1996-1997 Aleksey Volynskov
2 * Copyright (C) 2011 Rambo
3 * Copyright (C) 2020 SovietPony
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
18 #include "map.h"
20 #include "game.h"
21 #include "items.h"
22 #include "things.h"
23 #include "monster.h"
24 #include "switch.h"
25 #include "view.h"
27 #include "music.h"
28 #include "files.h"
29 #include "render.h"
31 #include <stdio.h>
32 #include <string.h>
33 #include <assert.h>
34 #include "my.h"
35 #include "error.h"
36 #include "cp866.h"
38 #include "common/streams.h"
39 #include "common/files.h"
41 enum {
42 MB_COMMENT = -1, MB_END = 0,
43 MB_WALLNAMES, MB_BACK, MB_WTYPE, MB_FRONT, MB_THING, MB_SWITCH,
44 MB_MUSIC, MB_SKY,
45 MB_SWITCH2,
46 MB__UNKNOWN
47 };
49 typedef struct map_header_t {
50 char id[8];
51 short ver;
52 } map_header_t;
54 typedef struct map_block_t {
55 short t;
56 short st;
57 int sz;
58 } map_block_t;
60 typedef struct old_thing_t {
61 short x, y;
62 short t;
63 unsigned short f;
64 } old_thing_t;
66 static map_block_t blk;
68 static int G_load (Reader *h) {
69 switch (blk.t) {
70 case MB_MUSIC:
71 stream_read(g_music, 8, 1, h);
72 //if (music_random) {
73 // F_randmus(g_music);
74 //}
75 MUS_load(g_music);
76 return 1;
77 }
78 return 0;
79 }
81 static int IT_load (Reader *h) {
82 int m, i, j;
83 old_thing_t t;
84 switch (blk.t) {
85 case MB_THING:
86 for (i = 0; blk.sz > 0; ++i, blk.sz -= 8) {
87 t.x = stream_read16(h);
88 t.y = stream_read16(h);
89 t.t = stream_read16(h);
90 t.f = stream_read16(h);
91 it[i].o.x = t.x;
92 it[i].o.y = t.y;
93 it[i].t = t.t;
94 it[i].s = t.f;
95 if (it[i].t && (it[i].s & THF_DM) && !g_dm) {
96 it[i].t=0;
97 }
98 }
99 m = i;
100 for (i = 0, j = -1; i < m; ++i) {
101 if (it[i].t == TH_PLR1) {
102 j = i;
103 it[i].t = 0;
106 if (!g_dm) {
107 if (j == -1) {
108 logo("Player 1 point not exists on the map\n");
109 return 0; // error
111 dm_pos[0].x = it[j].o.x;
112 dm_pos[0].y = it[j].o.y;
113 dm_pos[0].d = it[j].s & THF_DIR;
115 for (i = 0, j = -1; i < m; ++i) {
116 if (it[i].t == TH_PLR2) {
117 j = i;
118 it[i].t = 0;
121 if (!g_dm && _2pl) {
122 if (j == -1) {
123 logo("Player 2 point not exists on the map\n");
124 return 0; // error
126 dm_pos[1].x = it[j].o.x;
127 dm_pos[1].y = it[j].o.y;
128 dm_pos[1].d = it[j].s & THF_DIR;
130 for (i = 0, j = 0; i < m; ++i) {
131 if (it[i].t == TH_DMSTART) {
132 if (g_dm) {
133 dm_pos[j].x = it[i].o.x;
134 dm_pos[j].y = it[i].o.y;
135 dm_pos[j].d = it[i].s & THF_DIR;
137 it[i].t = 0;
138 ++j;
141 if (g_dm && j < 2) {
142 logo("Required at least two DM points on the map\n");
143 return 0; // error
145 if (g_dm) {
146 dm_pnum = j;
147 dm_pl1p = myrand(dm_pnum);
148 do {
149 dm_pl2p = myrand(dm_pnum);
150 } while (dm_pl2p == dm_pl1p);
151 } else {
152 dm_pl1p = 0;
153 dm_pl2p = 1;
154 dm_pnum = 2;
156 PL_spawn(&pl1, dm_pos[dm_pl1p].x, dm_pos[dm_pl1p].y, dm_pos[dm_pl1p].d);
157 if (_2pl) {
158 PL_spawn(&pl2, dm_pos[dm_pl2p].x, dm_pos[dm_pl2p].y, dm_pos[dm_pl2p].d);
160 for (i = 0; i < m; ++i) {
161 if (it[i].t >= TH_CLIP && it[i].t < TH_DEMON) {
162 it[i].s = 0;
163 it[i].t = it[i].t - TH_CLIP + I_CLIP;
164 if (it[i].t >= I_KEYR && it[i].t <= I_KEYB) {
165 it[i].t |= 0x8000;
167 } else if (it[i].t >= TH_DEMON) {
168 MN_spawn(it[i].o.x, it[i].o.y, it[i].s & THF_DIR, it[i].t - TH_DEMON + MN_DEMON);
169 it[i].t = 0;
172 return 1;
174 return 0;
177 static int SW_load (Reader *h) {
178 int i;
179 switch(blk.t) {
180 case MB_SWITCH2:
181 sw_secrets = 0;
182 for (i = 0; i < MAXSW && blk.sz > 0; ++i, blk.sz -= 9) {
183 sw[i].x = stream_read8(h);
184 sw[i].y = stream_read8(h);
185 sw[i].t = stream_read8(h);
186 sw[i].tm = stream_read8(h); // unused
187 sw[i].a = stream_read8(h);
188 sw[i].b = stream_read8(h);
189 sw[i].c = stream_read8(h);
190 sw[i].d = stream_read8(h); // unused
191 sw[i].f = stream_read8(h);
192 sw[i].tm = 0;
193 sw[i].d = 0;
194 sw[i].f |= 0x80;
195 if (sw[i].t == SW_SECRET) {
196 ++sw_secrets;
199 return 1;
201 return 0;
204 static void unpack (void *buf, int len, void *obuf) {
205 int i = 0;
206 int j = 0;
207 unsigned char *p = buf;
208 unsigned char *q = obuf;
209 while (i < len) {
210 int id = p[i];
211 int step = 1;
212 i += 1;
213 if (id == 0xff) {
214 step = p[i] | p[i + 1] << 8;
215 id = p[i + 2];
216 i += 3;
218 memset(&q[j], id, step);
219 j += step;
223 static int read_array (void *p, Reader *h) {
224 void *buf;
225 switch (blk.st) {
226 case 0:
227 stream_read(p, FLDW * FLDH, 1, h);
228 break;
229 case 1:
230 buf = malloc(blk.sz);
231 if (buf == NULL) {
232 logo("Out of memory\n");
233 return 0; // error
235 stream_read(buf, blk.sz, 1, h);
236 unpack(buf, blk.sz, p);
237 free(buf);
238 break;
239 default:
240 return 0;
242 return 1;
245 static int W_load (Reader *h) {
246 int i;
247 char s[8];
248 switch (blk.t) {
249 case MB_WALLNAMES:
250 R_begin_load();
251 memset(walf, 0, sizeof(walf));
252 for (i = 1; i < 256 && blk.sz > 0; i++, blk.sz -= 9) {
253 stream_read(s, 8, 1, h);
254 walf[i] = stream_read8(h) ? 1 : 0; // ???
255 R_load(s);
256 if (cp866_strncasecmp(s, "VTRAP01", 8) == 0) {
257 walf[i] |= 2;
260 R_end_load();
261 return 1;
262 case MB_BACK:
263 return read_array(fldb, h);
264 case MB_WTYPE:
265 return read_array(fld, h);
266 case MB_FRONT:
267 return read_array(fldf, h);
268 case MB_SKY:
269 sky_type = stream_read16(h);
270 R_loadsky(sky_type);
271 return 1;
273 return 0;
276 int MAP_load (Reader *r) {
277 assert(r != NULL);
278 int ok = 0;
279 map_header_t hdr;
280 W_init(); // reset all game data
281 stream_read(hdr.id, 8, 1, r);
282 hdr.ver = stream_read16(r);
283 if (memcmp(hdr.id, "Doom2D\x1A", 8) == 0) {
284 ok = 1;
285 while (ok) {
286 blk.t = stream_read16(r);
287 blk.st = stream_read16(r);
288 blk.sz = stream_read32(r);
289 long off = r->getpos(r) + blk.sz;
290 switch (blk.t) {
291 case MB_MUSIC:
292 ok = G_load(r);
293 break;
294 case MB_WALLNAMES:
295 case MB_BACK:
296 case MB_WTYPE:
297 case MB_FRONT:
298 case MB_SKY:
299 ok = W_load(r);
300 break;
301 case MB_THING:
302 ok = IT_load(r);
303 break;
304 case MB_SWITCH2:
305 ok = SW_load(r);
306 break;
307 case MB_COMMENT:
308 /* skip */
309 break;
310 case MB_END:
311 return ok;
312 default:
313 logo("Unknown block %d(%d)\n", blk.t, blk.st);
314 return 0; // error
316 r->setpos(r, off);
318 } else {
319 logo("Invalid map header\n");
320 ok = 0;
322 return ok;
325 void F_loadmap (char n[8]) {
326 FILE_Reader rd;
327 int r = F_getresid(n);
328 FILE *h = wadh[wad[r].f];
329 fseek(h, wad[r].o, SEEK_SET);
330 FILE_AssignReader(&rd, h);
331 if (!MAP_load(&rd.base)) {
332 ERR_fatal("Failed to load map");