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