1 // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
2 // Ken Silverman's official web site: "http://www.advsys.net/ken"
3 // See the included license file "BUILDLIC.TXT" for license info.
4 //
5 // This file has been modified from Ken Silverman's original release
6 // by Jonathon Fowler (jf@jonof.id.au)
7 // by the EDuke32 team (development@voidpoint.com)
8
9 #include "compat.h"
10 #include "build.h"
11 #include "baselayer.h"
12 #include "engine_priv.h"
13 #include "cache1d.h"
14 #include "lz4.h"
15 #include "crc32.h"
16
17 #include "vfs.h"
18
19 static void *g_vm_data;
20
21 // The tile file number (tilesXXX <- this) of each tile:
22 // 0 <= . < MAXARTFILES_BASE: tile is in a "base" ART file
23 // MAXARTFILES_BASE <= . < MAXARTFILES_TOTAL: tile is in a map-specific ART file
24 static uint8_t tilefilenum[MAXTILES];
25 EDUKE32_STATIC_ASSERT(MAXARTFILES_TOTAL <= 256);
26
27 static int32_t tilefileoffs[MAXTILES];
28
29 // Backup tilefilenum[] and tilefileoffs[]. These get allocated only when
30 // necessary (have per-map ART files).
31 static uint8_t *g_bakTileFileNum;
32 static int32_t *g_bakTileFileOffs;
33 static vec2_16_t *g_bakTileSiz;
34 static picanm_t *g_bakPicAnm;
35 static char * g_bakFakeTile;
36 static char ** g_bakFakeTileData;
37 static rottile_t *g_bakRottile;
38 // NOTE: picsiz[] is not backed up, but recalculated when necessary.
39
40 //static int32_t artsize = 0;
41 static int32_t g_vm_size = 0;
42
43 static char artfilename[BMAX_PATH];
44 static char artfilenameformat[BMAX_PATH];
45 static char mapartfilename[BMAX_PATH]; // map-specific ART file name
46 static int32_t mapartfnXXofs; // byte offset to 'XX' (the number part) in the above
47 static int32_t artfilnum, artfilplc;
48 static buildvfs_kfd artfil;
49
50 ////////// Per-map ART file loading //////////
51
52 // Some forward declarations.
53 static const char *artGetIndexedFileName(int32_t tilefilei);
54 static int32_t artReadIndexedFile(int32_t tilefilei);
55
artClearMapArtFilename(void)56 static inline void artClearMapArtFilename(void)
57 {
58 Bmemset(mapartfilename, 0, sizeof(mapartfilename));
59 mapartfnXXofs = 0;
60 }
61
artUpdateManifest(void)62 static inline void artUpdateManifest(void)
63 {
64 for (bssize_t i=0; i<MAXTILES; i++)
65 tileUpdatePicSiz(i);
66 }
67
68 template <typename origar_t, typename bakar_t>
RESTORE_MAPART_ARRAY(origar_t & origar,bakar_t & bakar)69 static inline void RESTORE_MAPART_ARRAY(origar_t & origar, bakar_t & bakar)
70 {
71 EDUKE32_STATIC_ASSERT(sizeof(origar[0]) == sizeof(bakar[0]));
72 for (size_t i=0; i < ARRAY_SIZE(origar); i++)
73 if (tilefilenum[i] >= MAXARTFILES_BASE)
74 origar[i] = bakar[i];
75 DO_FREE_AND_NULL(bakar);
76 }
77
78 template <typename origar_t, typename bakar_t>
ALLOC_MAPART_ARRAY(origar_t & origar,bakar_t & bakar)79 static inline void ALLOC_MAPART_ARRAY(origar_t & origar, bakar_t & bakar)
80 {
81 bakar = (bakar_t) Xmalloc(ARRAY_SIZE(origar) * sizeof(origar[0]));
82 Bmemcpy(bakar, origar, ARRAY_SIZE(origar) * sizeof(origar[0]));
83 }
84
artClearMapArt(void)85 void artClearMapArt(void)
86 {
87 if (g_bakTileFileNum == NULL)
88 return; // per-map ART N/A
89
90 artClearMapArtFilename();
91
92 if (artfilnum >= MAXARTFILES_BASE)
93 {
94 kclose(artfil);
95
96 artfil = buildvfs_kfd_invalid;
97 artfilnum = -1;
98 artfilplc = 0L;
99 }
100
101 for (bssize_t i=0; i<MAXTILES; i++)
102 {
103 if (tilefilenum[i] >= MAXARTFILES_BASE)
104 {
105 // XXX: OK way to free it? Better: cache1d API. CACHE1D_FREE
106 walock[i] = CACHE1D_FREE;
107 waloff[i] = 0;
108 }
109 }
110
111 // Restore original per-tile arrays
112 RESTORE_MAPART_ARRAY(tilefileoffs, g_bakTileFileOffs);
113 RESTORE_MAPART_ARRAY(tilesiz, g_bakTileSiz);
114 RESTORE_MAPART_ARRAY(picanm, g_bakPicAnm);
115 RESTORE_MAPART_ARRAY(rottile, g_bakRottile);
116
117 // restore entire faketile array as it can cause problems otherwise
118 EDUKE32_STATIC_ASSERT(sizeof(faketile[0]) == sizeof(g_bakFakeTile[0]));
119 Bmemcpy(faketile, g_bakFakeTile, ARRAY_SIZE(faketile) * sizeof(faketile[0]));
120 DO_FREE_AND_NULL(g_bakFakeTile);
121
122 for (size_t i = 0; i < MAXUSERTILES; ++i)
123 {
124 if (tilefilenum[i] >= MAXARTFILES_BASE && faketiledata[i] != g_bakFakeTileData[i])
125 {
126 Bfree(faketiledata[i]);
127 faketiledata[i] = g_bakFakeTileData[i];
128 }
129 }
130 DO_FREE_AND_NULL(g_bakFakeTileData);
131
132 // must be restored last
133 RESTORE_MAPART_ARRAY(tilefilenum, g_bakTileFileNum);
134
135 artUpdateManifest();
136 #ifdef USE_OPENGL
137 //POGOTODO: review this to ensure we're not invalidating more than we have to
138 gltexinvalidatetype(INVALIDATE_ART);
139 # ifdef POLYMER
140 if (videoGetRenderMode() == REND_POLYMER)
141 polymer_texinvalidate();
142 # endif
143 #endif
144 }
145
artSetupMapArt(const char * filename)146 void artSetupMapArt(const char *filename)
147 {
148 artClearMapArt();
149
150 if (Bstrlen(filename) + 7 >= sizeof(mapartfilename))
151 return;
152
153 Bstrcpy(mapartfilename, filename);
154 append_ext_UNSAFE(mapartfilename, "_XX.art");
155 mapartfnXXofs = Bstrlen(mapartfilename) - 6;
156
157 // Check for first per-map ART file: if that one doesn't exist, don't load any.
158 buildvfs_kfd fil = kopen4loadfrommod(artGetIndexedFileName(MAXARTFILES_BASE), 0);
159
160 if (fil == buildvfs_kfd_invalid)
161 {
162 artClearMapArtFilename();
163 return;
164 }
165
166 kclose(fil);
167
168 // Allocate backup arrays.
169 ALLOC_MAPART_ARRAY(tilefilenum, g_bakTileFileNum);
170 ALLOC_MAPART_ARRAY(tilefileoffs, g_bakTileFileOffs);
171 ALLOC_MAPART_ARRAY(tilesiz, g_bakTileSiz);
172 ALLOC_MAPART_ARRAY(picanm, g_bakPicAnm);
173 ALLOC_MAPART_ARRAY(faketile, g_bakFakeTile);
174 ALLOC_MAPART_ARRAY(faketiledata, g_bakFakeTileData);
175 ALLOC_MAPART_ARRAY(rottile, g_bakRottile);
176
177 for (bssize_t i=MAXARTFILES_BASE; i<MAXARTFILES_TOTAL; i++)
178 {
179 int ret = artReadIndexedFile(i);
180
181 if (ret != 0)
182 {
183 // NOTE: i == MAXARTFILES_BASE && ret == -1 can only have happened
184 // if the file was deleted between the above file existence check
185 // and now. Very cornerly... but I like my code to be prepared to
186 // any eventuality.
187 if (i == MAXARTFILES_BASE || ret != -1)
188 artClearMapArt();
189 break;
190 }
191 }
192
193 artUpdateManifest();
194 #ifdef USE_OPENGL
195 //POGOTODO: review this to ensure we're not invalidating more than we have to
196 gltexinvalidatetype(INVALIDATE_ART);
197 # ifdef POLYMER
198 if (videoGetRenderMode() == REND_POLYMER)
199 polymer_texinvalidate();
200 # endif
201 #endif
202 }
203
204 //
205 // ART loading
206 //
207
tileSetupDummy(int32_t const tile)208 void tileSetupDummy(int32_t const tile)
209 {
210 faketile[tile>>3] |= pow2char[tile&7];
211 DO_FREE_AND_NULL(faketiledata[tile]);
212 }
213
tileSetDataSafe(int32_t const tile,int32_t tsiz,char const * const buffer)214 static void tileSetDataSafe(int32_t const tile, int32_t tsiz, char const * const buffer)
215 {
216 int const compressed_tsiz = LZ4_compressBound(tsiz);
217 char * newtile = (char *) Xmalloc(compressed_tsiz);
218
219 if ((tsiz = LZ4_compress_default(buffer, newtile, tsiz, compressed_tsiz)) != -1)
220 {
221 faketilesize[tile] = tsiz;
222 faketiledata[tile] = (char *) Xrealloc(newtile, tsiz);
223 faketile[tile>>3] |= pow2char[tile&7];
224 tilefilenum[tile] = MAXARTFILES_TOTAL;
225 }
226 else
227 {
228 Bfree(newtile);
229 }
230 }
231
tileSetData(int32_t const tile,int32_t tsiz,char const * const buffer)232 void tileSetData(int32_t const tile, int32_t tsiz, char const * const buffer)
233 {
234 int const compressed_tsiz = LZ4_compressBound(tsiz);
235 faketiledata[tile] = (char *) Xrealloc(faketiledata[tile], compressed_tsiz);
236
237 if ((tsiz = LZ4_compress_default(buffer, faketiledata[tile], tsiz, compressed_tsiz)) != -1)
238 {
239 faketilesize[tile] = tsiz;
240 faketiledata[tile] = (char *) Xrealloc(faketiledata[tile], tsiz);
241 faketile[tile>>3] |= pow2char[tile&7];
242 tilefilenum[tile] = MAXARTFILES_TOTAL;
243 }
244 else
245 {
246 DO_FREE_AND_NULL(faketiledata[tile]);
247 faketile[tile>>3] &= ~pow2char[tile&7];
248 }
249 }
250
tileSoftDelete(int32_t const tile)251 static void tileSoftDelete(int32_t const tile)
252 {
253 tilesiz[tile].x = 0;
254 tilesiz[tile].y = 0;
255 picsiz[tile] = 0;
256
257 // CACHE1D_FREE
258 walock[tile] = CACHE1D_FREE;
259 waloff[tile] = 0;
260
261 faketile[tile>>3] &= ~pow2char[tile&7];
262
263 Bmemset(&picanm[tile], 0, sizeof(picanm_t));
264 }
265
tileDelete(int32_t const tile)266 void tileDelete(int32_t const tile)
267 {
268 tileSoftDelete(tile);
269
270 DO_FREE_AND_NULL(faketiledata[tile]);
271
272 vox_undefine(tile);
273
274 #ifdef USE_OPENGL
275 for (ssize_t i=MAXPALOOKUPS-1; i>=0; --i)
276 hicclearsubst(tile, i);
277
278 md_undefinetile(tile);
279 #endif
280 }
281
tileUpdatePicSiz(int32_t picnum)282 void tileUpdatePicSiz(int32_t picnum)
283 {
284 int j = 15;
285
286 while ((j > 1) && (pow2long[j] > tilesiz[picnum].x))
287 j--;
288 picsiz[picnum] = j;
289
290 j = 15;
291 while ((j > 1) && (pow2long[j] > tilesiz[picnum].y))
292 j--;
293 picsiz[picnum] |= j<<4;
294 }
295
tileSetSize(int32_t picnum,int16_t dasizx,int16_t dasizy)296 void tileSetSize(int32_t picnum, int16_t dasizx, int16_t dasizy)
297 {
298 tilesiz[picnum].x = dasizx;
299 tilesiz[picnum].y = dasizy;
300
301 tileUpdatePicSiz(picnum);
302 }
303
artReadHeader(buildvfs_kfd const fil,char const * const fn,artheader_t * const local)304 int32_t artReadHeader(buildvfs_kfd const fil, char const * const fn, artheader_t * const local)
305 {
306 int32_t artversion;
307 kread(fil, &artversion, 4); artversion = B_LITTLE32(artversion);
308
309 if (artversion == B_LITTLE32(0x4c495542))
310 {
311 kread(fil, &artversion, 4); artversion = B_LITTLE32(artversion);
312 if (artversion == B_LITTLE32(0x54524144))
313 {
314 kread(fil, &artversion, 4); artversion = B_LITTLE32(artversion);
315 }
316 else
317 {
318 initprintf("loadpics: Invalid art file, %s\n", fn);
319 kclose(fil);
320 return 1;
321 }
322 }
323
324 if (artversion != 1)
325 {
326 initprintf("loadpics: Invalid art file version in %s\n", fn);
327 kclose(fil);
328 return 1;
329 }
330
331 int32_t numtiles_dummy;
332 kread(fil, &numtiles_dummy, 4);
333
334 kread(fil, &local->tilestart, 4); local->tilestart = B_LITTLE32(local->tilestart);
335 kread(fil, &local->tileend, 4); local->tileend = B_LITTLE32(local->tileend);
336
337 if ((uint32_t) local->tilestart >= MAXUSERTILES || (uint32_t) local->tileend >= MAXUSERTILES)
338 {
339 initprintf("loadpics: Invalid localtilestart or localtileend in %s\n", fn);
340 kclose(fil);
341 return 1;
342 }
343 if (local->tileend < local->tilestart)
344 {
345 initprintf("loadpics: localtileend < localtilestart in %s\n", fn);
346 kclose(fil);
347 return 1;
348 }
349
350 local->numtiles = (local->tileend-local->tilestart+1);
351
352 return 0;
353 }
354
artReadHeaderFromBuffer(uint8_t const * const buf,artheader_t * const local)355 int32_t artReadHeaderFromBuffer(uint8_t const * const buf, artheader_t * const local)
356 {
357 int const artversion = B_LITTLE32(B_UNBUF32(&buf[0]));
358 if (EDUKE32_PREDICT_FALSE(artversion != 1))
359 {
360 initprintf("loadpics: Invalid art file version\n");
361 return 1;
362 }
363
364 local->tilestart = B_LITTLE32(B_UNBUF32(&buf[8]));
365 local->tileend = B_LITTLE32(B_UNBUF32(&buf[12]));
366
367 if (EDUKE32_PREDICT_FALSE((unsigned) local->tilestart >= MAXUSERTILES || (unsigned) local->tileend >= MAXUSERTILES))
368 {
369 initprintf("loadpics: Invalid localtilestart or localtileend\n");
370 return 1;
371 }
372 if (EDUKE32_PREDICT_FALSE(local->tileend < local->tilestart))
373 {
374 initprintf("loadpics: localtileend < localtilestart\n");
375 return 1;
376 }
377
378 local->numtiles = (local->tileend-local->tilestart+1);
379
380 return 0;
381 }
382
artCheckUnitFileHeader(uint8_t const * const buf,int32_t length)383 int32_t artCheckUnitFileHeader(uint8_t const * const buf, int32_t length)
384 {
385 if (EDUKE32_PREDICT_FALSE(length <= ARTv1_UNITOFFSET))
386 return -1;
387
388 artheader_t local;
389 if (EDUKE32_PREDICT_FALSE(artReadHeaderFromBuffer(buf, &local) != 0))
390 return -2;
391
392 if (EDUKE32_PREDICT_FALSE(local.numtiles != 1))
393 return -3;
394
395 return 0;
396 }
397
tileConvertAnimFormat(int32_t const picnum,uint32_t const picanmdisk)398 void tileConvertAnimFormat(int32_t const picnum, uint32_t const picanmdisk)
399 {
400 EDUKE32_STATIC_ASSERT(PICANM_ANIMTYPE_MASK == 192);
401
402 picanm_t * const thispicanm = &picanm[picnum];
403 thispicanm->num = picanmdisk&63;
404 thispicanm->xofs = (picanmdisk>>8)&255;
405 thispicanm->yofs = (picanmdisk>>16)&255;
406 thispicanm->sf = ((picanmdisk>>24)&15) | (picanmdisk&192);
407 thispicanm->extra = (picanmdisk>>28)&15;
408 }
409
artReadManifest(buildvfs_kfd const fil,artheader_t const * const local)410 void artReadManifest(buildvfs_kfd const fil, artheader_t const * const local)
411 {
412 int16_t *tilesizx = (int16_t *) Xmalloc(local->numtiles * sizeof(int16_t));
413 int16_t *tilesizy = (int16_t *) Xmalloc(local->numtiles * sizeof(int16_t));
414 kread(fil, tilesizx, local->numtiles*sizeof(int16_t));
415 kread(fil, tilesizy, local->numtiles*sizeof(int16_t));
416
417 for (bssize_t i=local->tilestart; i<=local->tileend; i++)
418 {
419 tilesiz[i].x = B_LITTLE16(tilesizx[i-local->tilestart]);
420 tilesiz[i].y = B_LITTLE16(tilesizy[i-local->tilestart]);
421
422 uint32_t picanmdisk;
423 kread(fil, &picanmdisk, sizeof(uint32_t));
424 picanmdisk = B_LITTLE32(picanmdisk);
425 tileConvertAnimFormat(i, picanmdisk);
426 }
427
428 DO_FREE_AND_NULL(tilesizx);
429 DO_FREE_AND_NULL(tilesizy);
430 }
431
artPreloadFile(buildvfs_kfd const fil,artheader_t const * const local)432 void artPreloadFile(buildvfs_kfd const fil, artheader_t const * const local)
433 {
434 char *buffer = NULL;
435 int32_t buffersize = 0;
436
437 for (bssize_t i=local->tilestart; i<=local->tileend; i++)
438 {
439 int const dasiz = tilesiz[i].x * tilesiz[i].y;
440
441 if (dasiz == 0)
442 {
443 tileDelete(i);
444 continue;
445 }
446
447 maybe_grow_buffer(&buffer, &buffersize, dasiz);
448 kread(fil, buffer, dasiz);
449 tileSetData(i, dasiz, buffer);
450 }
451
452 DO_FREE_AND_NULL(buffer);
453 }
454
artPreloadFileSafe(buildvfs_kfd const fil,artheader_t const * const local)455 static void artPreloadFileSafe(buildvfs_kfd const fil, artheader_t const * const local)
456 {
457 char *buffer = NULL;
458 int32_t buffersize = 0;
459
460 for (bssize_t i=local->tilestart; i<=local->tileend; i++)
461 {
462 int const dasiz = tilesiz[i].x * tilesiz[i].y;
463
464 if (dasiz == 0)
465 {
466 tileSoftDelete(i);
467 continue;
468 }
469
470 maybe_grow_buffer(&buffer, &buffersize, dasiz);
471 kread(fil, buffer, dasiz);
472 tileSetDataSafe(i, dasiz, buffer);
473 }
474
475 DO_FREE_AND_NULL(buffer);
476 }
477
artGetIndexedFileName(int32_t tilefilei)478 static const char *artGetIndexedFileName(int32_t tilefilei)
479 {
480 if (tilefilei >= MAXARTFILES_BASE)
481 {
482 int32_t o = mapartfnXXofs;
483 tilefilei -= MAXARTFILES_BASE;
484
485 mapartfilename[o+1] = '0' + tilefilei%10;
486 mapartfilename[o+0] = '0' + (tilefilei/10)%10;
487
488 return mapartfilename;
489 }
490 else
491 {
492 Bsnprintf(artfilename, sizeof(artfilename), artfilenameformat, tilefilei);
493
494 return artfilename;
495 }
496 }
497
498 // Returns:
499 // 0: successfully read ART file
500 // >0: error with the ART file
501 // -1: ART file does not exist
502 //<-1: per-map ART issue
artReadIndexedFile(int32_t tilefilei)503 static int32_t artReadIndexedFile(int32_t tilefilei)
504 {
505 const char *fn = artGetIndexedFileName(tilefilei);
506 const int32_t permap = (tilefilei >= MAXARTFILES_BASE); // is it a per-map ART file?
507 buildvfs_kfd fil;
508
509 if ((fil = kopen4load(fn, 0)) != buildvfs_kfd_invalid)
510 {
511 artheader_t local;
512 int const headerval = artReadHeader(fil, fn, &local);
513 if (headerval != 0)
514 {
515 kclose(fil);
516 return headerval;
517 }
518
519 if (permap)
520 {
521 // Check whether we can evict existing tiles to make place for
522 // per-map ART ones.
523 for (int i=local.tilestart; i<=local.tileend; i++)
524 {
525 // Tiles having dummytile replacements or those that are
526 // cache1d-locked can't be replaced.
527 if (faketile[i>>3] & pow2char[i&7] || walock[i] >= CACHE1D_LOCKED)
528 {
529 initprintf("loadpics: per-map ART file \"%s\": "
530 "tile %d has dummytile or is locked\n", fn, i);
531 kclose(fil);
532 return -3;
533 }
534 }
535
536 // Free existing tiles from the cache1d. CACHE1D_FREE
537 Bmemset(&waloff[local.tilestart], 0, local.numtiles*sizeof(intptr_t));
538 Bmemset(&walock[local.tilestart], 1, local.numtiles*sizeof(walock[0]));
539 }
540
541 artReadManifest(fil, &local);
542
543 #ifndef USE_PHYSFS
544 if (cache1d_file_fromzip(fil))
545 #else
546 if (1)
547 #endif
548 {
549 if (permap)
550 artPreloadFileSafe(fil, &local);
551 else
552 artPreloadFile(fil, &local);
553 }
554 else
555 {
556 int offscount = ktell(fil);
557
558 for (bssize_t i=local.tilestart; i<=local.tileend; ++i)
559 {
560 int const dasiz = tilesiz[i].x * tilesiz[i].y;
561
562 tilefilenum[i] = tilefilei;
563 tilefileoffs[i] = offscount;
564
565 offscount += dasiz;
566 // artsize += ((dasiz+15)&0xfffffff0);
567 }
568 }
569
570 #ifdef DEBUGGINGAIDS
571 if (permap)
572 initprintf("Read in per-map ART file \"%s\"\n", fn);
573 #endif
574 kclose(fil);
575 return 0;
576 }
577
578 return -1;
579 }
580
581 //
582 // loadpics
583 //
artLoadFiles(const char * filename,int32_t askedsize)584 int32_t artLoadFiles(const char *filename, int32_t askedsize)
585 {
586 Bstrncpyz(artfilenameformat, filename, sizeof(artfilenameformat));
587
588 Bmemset(&tilesiz[0], 0, sizeof(vec2_16_t) * MAXTILES);
589 Bmemset(picanm, 0, sizeof(picanm));
590
591 for (auto &rot : rottile)
592 rot = { -1, -1 };
593
594 // artsize = 0;
595
596 for (int tilefilei=0; tilefilei<MAXARTFILES_BASE; tilefilei++)
597 artReadIndexedFile(tilefilei);
598
599 Bmemset(gotpic, 0, sizeof(gotpic));
600 //cachesize = min((int32_t)((Bgetsysmemsize()/100)*60),max(artsize,askedsize));
601 g_vm_size = (Bgetsysmemsize() <= (uint32_t)askedsize) ? (int32_t)((Bgetsysmemsize() / 100) * 60) : askedsize;
602 zpl_virtual_memory vm = Xvm_alloc(nullptr, g_vm_size);
603 g_vm_data = vm.data;
604 g_vm_size = vm.size;
605 g_cache.initBuffer((intptr_t) g_vm_data, g_vm_size);
606
607 artUpdateManifest();
608
609 artfil = buildvfs_kfd_invalid;
610 artfilnum = -1;
611 artfilplc = 0L;
612
613 return 0;
614 }
615
616
617 //
618 // loadtile
619 //
620 static void tilePostLoad(int16_t tilenume);
621
622 bool (*rt_tileload_callback)(int16_t tileNum) = nullptr;
623
tileLoad(int16_t tileNum)624 bool tileLoad(int16_t tileNum)
625 {
626 if ((unsigned) tileNum >= (unsigned) MAXTILES) return 0;
627 int const dasiz = tilesiz[tileNum].x*tilesiz[tileNum].y;
628 if (dasiz <= 0) return 0;
629
630 // Allocate storage if necessary.
631 if (waloff[tileNum] == 0)
632 {
633 walock[tileNum] = CACHE1D_UNLOCKED;
634 g_cache.allocateBlock(&waloff[tileNum], dasiz, &walock[tileNum]);
635 }
636
637 if (!duke64 || !rt_tileload_callback || !rt_tileload_callback(tileNum))
638 tileLoadData(tileNum, dasiz, (char *) waloff[tileNum]);
639
640 #ifdef USE_OPENGL
641 if (videoGetRenderMode() >= REND_POLYMOST &&
642 in3dmode())
643 {
644 //POGOTODO: this type stuff won't be necessary down the line -- review this
645 int type;
646 for (type = 0; type <= 1; ++type)
647 {
648 gltexinvalidate(tileNum, 0, (type ? DAMETH_CLAMPED : DAMETH_MASK) | DAMETH_INDEXED);
649 texcache_fetch(tileNum, 0, 0, (type ? DAMETH_CLAMPED : DAMETH_MASK) | DAMETH_INDEXED);
650 }
651 }
652 #endif
653
654 tilePostLoad(tileNum);
655
656 return (waloff[tileNum] != 0 && tilesiz[tileNum].x > 0 && tilesiz[tileNum].y > 0);
657 }
658
tileMaybeRotate(int16_t tilenume)659 void tileMaybeRotate(int16_t tilenume)
660 {
661 auto &rot = rottile[tilenume];
662 auto &siz = tilesiz[rot.owner];
663
664 auto src = (char *)waloff[rot.owner];
665 auto dst = (char *)waloff[tilenume];
666
667 // the engine has a squarerotatetile() we could call, but it mirrors at the same time
668 for (int x = 0; x < siz.x; ++x)
669 {
670 int const xofs = siz.x - x - 1;
671 int const yofs = siz.y * x;
672
673 for (int y = 0; y < siz.y; ++y)
674 *(dst + y * siz.x + xofs) = *(src + y + yofs);
675 }
676
677 tileSetSize(tilenume, siz.y, siz.x);
678 }
679
tileLoadData(int16_t tilenume,int32_t dasiz,char * buffer)680 void tileLoadData(int16_t tilenume, int32_t dasiz, char *buffer)
681 {
682 int const owner = rottile[tilenume].owner;
683
684 if (owner != -1)
685 {
686 if (!waloff[owner])
687 tileLoad(owner);
688
689 if (waloff[tilenume])
690 tileMaybeRotate(tilenume);
691
692 return;
693 }
694
695 int const tfn = tilefilenum[tilenume];
696
697 // dummy tiles for highres replacements and tilefromtexture definitions
698 if (faketile[tilenume>>3] & pow2char[tilenume&7])
699 {
700 if (faketiledata[tilenume] != NULL)
701 LZ4_decompress_safe(faketiledata[tilenume], buffer, faketilesize[tilenume], dasiz);
702
703 faketimerhandler();
704 return;
705 }
706
707 // Potentially switch open ART file.
708 if (tfn != artfilnum)
709 {
710 if (artfil != buildvfs_kfd_invalid)
711 kclose(artfil);
712
713 char const *fn = artGetIndexedFileName(tfn);
714
715 artfil = kopen4loadfrommod(fn, 0);
716
717 if (artfil == buildvfs_kfd_invalid)
718 {
719 initprintf("Failed opening ART file \"%s\"!\n", fn);
720 return;
721 }
722
723 artfilnum = tfn;
724 artfilplc = 0L;
725
726 faketimerhandler();
727 }
728
729 // Seek to the right position.
730 if (artfilplc != tilefileoffs[tilenume])
731 {
732 klseek(artfil, tilefileoffs[tilenume], BSEEK_SET);
733 faketimerhandler();
734 }
735
736 kread(artfil, buffer, dasiz);
737 faketimerhandler();
738 artfilplc = tilefileoffs[tilenume]+dasiz;
739 }
740
tilePostLoad(int16_t tilenume)741 static void tilePostLoad(int16_t tilenume)
742 {
743 #if !defined DEBUG_TILESIZY_512 && !defined DEBUG_TILEOFFSETS
744 UNREFERENCED_PARAMETER(tilenume);
745 #endif
746 #ifdef DEBUG_TILESIZY_512
747 if (tilesizy[tilenume] >= 512)
748 {
749 int32_t i;
750 char *p = (char *) waloff[tilenume];
751 for (i=0; i<tilesizx[tilenume]*tilesizy[tilenume]; i++)
752 p[i] = i;
753 }
754 #endif
755 #ifdef DEBUG_TILEOFFSETS
756 // Add some dark blue marker lines to STEAM and CEILINGSTEAM.
757 // See test_tileoffsets.map.
758 if (tilenume==1250 || tilenume==1255)
759 {
760 char *p = (char *) waloff[tilenume];
761 p[0] = p[1] = p[2] = p[3] = 254;
762 }
763
764 // Add some offset to the cocktail glass neon sign. It's more asymmetric
765 // than the steam, and thus more suited to debugging the spatial
766 // orientation of drawn sprites.
767 if (tilenume==1008)
768 {
769 picanm[tilenume].xofs = 8;
770 picanm[tilenume].yofs = 12;
771 }
772 #endif
773 }
774
tileGetCRC32(int16_t tileNum)775 int32_t tileGetCRC32(int16_t tileNum)
776 {
777 if ((unsigned)tileNum >= (unsigned)MAXTILES)
778 return 0;
779 int const dasiz = tilesiz[tileNum].x * tilesiz[tileNum].y;
780 if (dasiz <= 0)
781 return 0;
782
783 auto data = (char *)Xmalloc(dasiz);
784 tileLoadData(tileNum, dasiz, data);
785
786 int32_t crc = Bcrc32((unsigned char *)data, (unsigned int)dasiz, 0);
787
788 Xfree(data);
789
790 return crc;
791 }
792
tileGetSize(int16_t tileNum)793 vec2_16_t tileGetSize(int16_t tileNum)
794 {
795 if ((unsigned)tileNum >= (unsigned)MAXTILES)
796 return vec2_16_t{};
797
798 return tilesiz[tileNum];
799 }
800
801 // Assumes pic has been initialized to zero.
artConvertRGB(palette_t * const pic,uint8_t const * const buf,int32_t const bufsizx,int32_t const sizx,int32_t const sizy)802 void artConvertRGB(palette_t * const pic, uint8_t const * const buf, int32_t const bufsizx, int32_t const sizx, int32_t const sizy)
803 {
804 for (bssize_t y = 0; y < sizy; ++y)
805 {
806 palette_t * const picrow = &pic[bufsizx * y];
807
808 for (bssize_t x = 0; x < sizx; ++x)
809 {
810 uint8_t index = buf[sizy * x + y];
811
812 if (index == 255)
813 continue;
814
815 index *= 3;
816
817 // pic is BGRA
818 picrow[x].r = palette[index+2];
819 picrow[x].g = palette[index+1];
820 picrow[x].b = palette[index];
821 picrow[x].f = 255;
822 }
823 }
824 }
825
826 //
827 // allocatepermanenttile
828 //
tileCreate(int16_t tilenume,int32_t xsiz,int32_t ysiz)829 intptr_t tileCreate(int16_t tilenume, int32_t xsiz, int32_t ysiz)
830 {
831 if (xsiz <= 0 || ysiz <= 0 || (unsigned) tilenume >= MAXTILES)
832 return 0;
833
834 int const dasiz = xsiz*ysiz;
835
836 walock[tilenume] = CACHE1D_PERMANENT;
837 g_cache.allocateBlock(&waloff[tilenume], dasiz, &walock[tilenume]);
838
839 tileSetSize(tilenume, xsiz, ysiz);
840 Bmemset(&picanm[tilenume], 0, sizeof(picanm_t));
841
842 return waloff[tilenume];
843 }
844
845 //
846 // copytilepiece
847 //
tileCopySection(int32_t tilenume1,int32_t sx1,int32_t sy1,int32_t xsiz,int32_t ysiz,int32_t tilenume2,int32_t sx2,int32_t sy2)848 void tileCopySection(int32_t tilenume1, int32_t sx1, int32_t sy1, int32_t xsiz, int32_t ysiz,
849 int32_t tilenume2, int32_t sx2, int32_t sy2)
850 {
851 char *ptr1, *ptr2, dat;
852 int32_t xsiz1, ysiz1, xsiz2, ysiz2, i, j, x1, y1, x2, y2;
853
854 xsiz1 = tilesiz[tilenume1].x; ysiz1 = tilesiz[tilenume1].y;
855 xsiz2 = tilesiz[tilenume2].x; ysiz2 = tilesiz[tilenume2].y;
856 if ((xsiz1 > 0) && (ysiz1 > 0) && (xsiz2 > 0) && (ysiz2 > 0))
857 {
858 if (waloff[tilenume1] == 0) tileLoad(tilenume1);
859 if (waloff[tilenume2] == 0) tileLoad(tilenume2);
860
861 x1 = sx1;
862 for (i=0; i<xsiz; i++)
863 {
864 y1 = sy1;
865 for (j=0; j<ysiz; j++)
866 {
867 x2 = sx2+i;
868 y2 = sy2+j;
869 if ((x2 >= 0) && (y2 >= 0) && (x2 < xsiz2) && (y2 < ysiz2))
870 {
871 ptr1 = (char *) (waloff[tilenume1] + x1*ysiz1 + y1);
872 ptr2 = (char *) (waloff[tilenume2] + x2*ysiz2 + y2);
873 dat = *ptr1;
874 if (dat != 255)
875 *ptr2 = *ptr1;
876 }
877
878 y1++; if (y1 >= ysiz1) y1 = 0;
879 }
880 x1++; if (x1 >= xsiz1) x1 = 0;
881 }
882 }
883 }
884
Buninitart(void)885 void Buninitart(void)
886 {
887 if (artfil != buildvfs_kfd_invalid)
888 kclose(artfil);
889
890 Xvm_free(zpl_vm(g_vm_data, g_vm_size));
891 }
892