2 * Copyright (C) Prikol Software 1996-1997
3 * Copyright (C) Aleksey Volynskov 1996-1997
4 * Copyright (C) <ARembo@gmail.com> 2011
5 * Copyright (C) fgsfds 2019
7 * This file is part of the Doom2D PSX project.
9 * Doom2D PSX is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
13 * Doom2D PSX is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/> or
20 * write to the Free Software Foundation, Inc.,
21 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 #define PACKET_MAX 40000
46 #define PSXRGB(r, g, b) ((((b) >> 3) << 10) | (((g) >> 3) << 5) | ((r) >> 3))
48 static u_char loadimg
[] = {
49 #include "loadpic.inc"
53 unsigned short clut_id
[NUMPALS
];
54 unsigned char clut_tab
[NUMPALS
] = {
55 0x00, 0x18, 0x20, 0x40, 0x58,
56 0x60, 0x80, 0xB0, 0xC0, 0xD0,
62 int cx1
, cx2
, cy1
, cy2
;
64 char fullscreen
= OFF
;
66 static struct drawbuf
{
69 u_char gpudata
[PACKET_MAX
];
73 struct drawbuf
*curbuf
= dbuf
+ 0;
74 static u_char
*primptr
;
75 static unsigned short curtpage
= 0;
77 static int bufidx
= 0;
80 #define MAX_CACHEDPICS 1024
81 #define CACHE_STARTX 320 // is already 64-aligned
82 #define CACHE_WIDTH 704 // 1024 - CACHE_STARTX
83 #define CACHE_HEIGHT 512
85 static cacheimg piccache
[MAX_CACHEDPICS
];
86 static u_short vram_alloc
[CACHE_WIDTH
];
91 unsigned short *clut
, *cp
;
96 SetDefDispEnv(&dbuf
[0].dispenv
, 0, 0, SCRW
, SCRH
);
97 SetDefDrawEnv(&dbuf
[0].drawenv
, 0, SCRH
, SCRW
, SCRH
);
98 SetDefDispEnv(&dbuf
[1].dispenv
, 0, SCRH
, SCRW
, SCRH
);
99 SetDefDrawEnv(&dbuf
[1].drawenv
, 0, 0, SCRW
, SCRH
);
101 for (i
= 0; i
< 2; ++i
) {
102 dbuf
[i
].drawenv
.isbg
= 1;
103 dbuf
[i
].drawenv
.dtd
= 0;
104 dbuf
[i
].drawenv
.dfe
= 1;
105 dbuf
[i
].dispenv
.screen
.w
= 256; // in "display" coordinates
106 dbuf
[i
].dispenv
.screen
.h
= SCRH
;
107 dbuf
[i
].dispenv
.screen
.x
= 0;
108 dbuf
[i
].dispenv
.screen
.y
= SCRY
;
111 PutDispEnv(&dbuf
[0].dispenv
);
112 PutDrawEnv(&dbuf
[0].drawenv
);
114 // clear the splash leftovers and shit
117 ClearImage(&drawclip
, 0, 0, 0);
120 // upload cluts into vram
122 cp
= clut
= malloc3(2 * 256);
123 for (i
= 0; i
< 256; ++i
, ++cp
, p
+= 3) {
127 *cp
= PSXRGB(*(p
+ 0), *(p
+ 1), *(p
+ 2));
128 if (i
&& !*cp
) *cp
= 0x8000;
131 clut_id
[0] = LoadClut((u_long
*)clut
, VRAM_PAL_X
, VRAM_PAL_Y
);
132 // generate several variations for player colors and shit
133 for (i
= 1; i
< NUMPALS
; ++i
) {
134 for (j
= 0x70; j
< 0x80; ++j
)
135 clut
[j
] = clut
[j
- 0x70 + clut_tab
[i
]];
136 clut_id
[i
] = LoadClut((u_long
*)clut
, VRAM_PAL_X
, VRAM_PAL_Y
+i
);
141 ClearOTag(dbuf
[0].ot
, OT_LEN
);
142 ClearOTag(dbuf
[1].ot
, OT_LEN
);
146 printf("V_init(): initialized video at %dx%dx16\n", SCRW
, SCRH
);
155 void V_setclip(short x
, short w
, short y
, short h
) {
157 drawclip
.y
= y
+ !bufidx
* SCRH
;
160 SetDrawArea((DR_AREA
*)primptr
, &drawclip
);
161 addPrim(curbuf
->ot
+ curot
, primptr
);
162 primptr
+= sizeof(DR_AREA
);
163 if (curot
< OT_LEN
-1) ++curot
;
166 void V_setrect(short x
, short w
, short y
, short h
) {
181 void V_center(int f
) {
183 V_offset(SCRW
/ 2 - 320 / 2, SCRH
/ 2 - 200 / 2);
188 void V_offset(int ox
, int oy
) {
193 static void draw_spr(short x
, short y
, cacheimg
*i
, int d
, int c
) {
197 if (!i
|| !i
->img
|| (primptr
+ sizeof(POLY_FT4
) >= curbuf
->gpudata
+ PACKET_MAX
))
198 return; // trying to draw nothing or ran out of prim buffer
205 V_uploadpic(i
); // attempt to upload right away so as to not cause slowdown later
207 if (d
& 1) x
= x
- w
+ sx
+ ofsx
; else x
= x
- sx
+ ofsx
;
208 if (d
& 2) y
= y
- h
+ sy
+ ofsy
; else y
= y
- sy
+ ofsy
;
210 if (x
+ w
< cx1
|| x
>= cx2
|| y
+ h
< cy1
|| y
>= cy2
)
211 return; // out of sight, out of mind
213 prim
= (POLY_FT4
*)primptr
; primptr
+= sizeof(POLY_FT4
);
216 prim
->tpage
= i
->tpage
;
217 prim
->clut
= clut_id
[c
];
218 setRGB0(prim
, 128, 128, 128);
220 prim
->x0
= prim
->x2
= x
;
221 prim
->y0
= prim
->y1
= y
;
222 prim
->x1
= prim
->x3
= x
+ w
;
223 prim
->y2
= prim
->y3
= y
+ h
;
225 if (w
> 255) w
= 255;
226 if (h
> 255) h
= 255;
230 prim
->u1
= prim
->u3
= i
->u
;
231 prim
->u0
= prim
->u2
= i
->u
+ w
- 1;
233 prim
->u0
= prim
->u2
= i
->u
;
234 prim
->u1
= prim
->u3
= i
->u
+ w
;
238 prim
->v2
= prim
->v3
= i
->v
;
239 prim
->v0
= prim
->v1
= i
->v
+ h
- 1;
241 prim
->v0
= prim
->v1
= i
->v
;
242 prim
->v2
= prim
->v3
= i
->v
+ h
;
245 addPrim(curbuf
->ot
+ curot
, prim
);
246 if (curot
< OT_LEN
-1) ++curot
;
249 // use this to draw textures more than 256 in width
250 // still limited to 256 height
251 void V_bigpic(int x
, int y
, cacheimg
*i
) {
264 // 1 tpage is normally 64 pixels wide, but our textures are 8bpp,
265 // so it's basically 128 wide
266 for (xx
= 0; xx
< w
; xx
+= 256, tpage
+= 2) {
267 prim
= (POLY_FT4
*)primptr
; primptr
+= sizeof(POLY_FT4
);
270 prim
->clut
= clut_id
[0];
271 setRGB0(prim
, 128, 128, 128);
273 if (ww
> 256) ww
= 256;
274 prim
->x0
= prim
->x2
= x
+ xx
;
275 prim
->y0
= prim
->y1
= y
;
276 prim
->x1
= prim
->x3
= x
+ xx
+ ww
;
277 prim
->y2
= prim
->y3
= y
+ h
;
278 prim
->u0
= prim
->u2
= i
->u
;
279 prim
->u1
= prim
->u3
= i
->u
+ ww
- 1;
280 prim
->v0
= prim
->v1
= i
->v
;
281 prim
->v2
= prim
->v3
= i
->v
+ h
;
282 addPrim(curbuf
->ot
+ curot
, prim
);
285 if (curot
< OT_LEN
-1) ++curot
;
288 void V_rotspr(int x
, int y
, cacheimg
*i
, int d
) {
289 x
+= i
->img
->w
* ((d
& 1) ? 1 : 0);
290 y
+= i
->img
->h
* ((d
& 2) ? 1 : 0);
291 draw_spr(x
, y
, i
, d
, 0);
294 void V_pic(short x
, short y
, cacheimg
* i
) {
295 draw_spr(x
, y
, i
, 0, 0);
298 void V_manspr(int x
, int y
, cacheimg
* p
, unsigned char c
) {
299 draw_spr(x
, y
, p
, 0, c
);
302 void V_manspr2(int x
, int y
, cacheimg
* p
, unsigned char c
) {
303 draw_spr(x
, y
, p
, 1, c
);
306 // вывести точку цвета c в координатах (x,y)
307 void V_dot(short x
, short y
, unsigned char c
) {
310 if (x
< cx1
|| x
>= cx2
|| y
< cy1
|| y
>= cy2
)
313 prim
= (TILE_1
*)primptr
;
314 primptr
+= sizeof(TILE_1
);
319 prim
->r0
= main_pal
[c
].r
;
320 prim
->g0
= main_pal
[c
].g
;
321 prim
->b0
= main_pal
[c
].b
;
323 addPrim(curbuf
->ot
+ curot
, prim
);
326 void smoke_sprf(int x
, int y
, byte c
) {
329 void flame_sprf(int x
, int y
, byte c
) {
332 void V_spr(short x
, short y
, cacheimg
* i
) {
333 draw_spr(x
, y
, i
, 0, 0);
336 void V_spr2(short x
, short y
, cacheimg
* i
) {
337 draw_spr(x
, y
, i
, 1, 0);
340 void V_clr(short x
, short w
, short y
, short h
, unsigned char c
) {
343 prim
= (TILE
*)primptr
;
344 primptr
+= sizeof(TILE
);
349 prim
->r0
= main_pal
[c
].r
;
350 prim
->g0
= main_pal
[c
].g
;
351 prim
->b0
= main_pal
[c
].b
;
353 addPrim(curbuf
->ot
+ curot
, prim
);
354 if (curot
< OT_LEN
-1) ++curot
;
357 void V_transclr(short x
, short w
, short y
, short h
, unsigned char c
) {
360 prim
= (TILE
*)primptr
;
361 primptr
+= sizeof(TILE
);
366 prim
->r0
= main_pal
[c
].r
;
367 prim
->g0
= main_pal
[c
].g
;
368 prim
->b0
= main_pal
[c
].b
;
369 setSemiTrans(prim
, 1);
371 addPrim(curbuf
->ot
+ curot
, prim
);
372 if (curot
< OT_LEN
-1) ++curot
;
375 // установить палитру из массива p
376 void VP_setall(void * p
) {
380 // установить n цветов, начиная с f, из массива p
381 void VP_set(void * p
, short f
, short n
) {
384 // установить адрес экранного буфера
385 // NULL - реальный экран
386 void V_setscr(void * p
) {
389 // скопировать прямоугольник на экран
390 void V_copytoscr(short x
, short w
, short y
, short h
) {
393 void V_maptoscr(int x
, int w
, int y
, int h
, void * cmap
) {
396 void V_remap_rect(int x
, int y
, int w
, int h
, byte
* cmap
) {
399 extern void * walp
[256];
401 static inline void draw_wall(cacheimg
*pic
, int sx
, int sy
) {
411 if (sx
+ w
< cx1
|| sx
> cx2
|| sy
+ h
< cy1
|| sy
> cy2
)
412 return; // have to do this because some pics are big so the border is 96px
414 // TODO: this is really fucking bad, fix the SPRT shit below and use it instead
415 prim
= (POLY_FT4
*)primptr
;
416 primptr
+= sizeof(POLY_FT4
);
420 prim
->tpage
= pic
->tpage
;
422 prim
->x0
= prim
->x2
= sx
;
423 prim
->y0
= prim
->y1
= sy
;
424 prim
->x1
= prim
->x3
= sx
+ w
;
425 prim
->y2
= prim
->y3
= sy
+ h
;
427 prim
->u0
= prim
->u2
= pic
->u
;
428 prim
->v0
= prim
->v1
= pic
->v
;
429 prim
->u1
= prim
->u3
= pic
->u
+ w
;
430 prim
->v2
= prim
->v3
= pic
->v
+ h
;
431 prim
->clut
= clut_id
[0];
432 setRGB0(prim
, 128, 128, 128);
434 addPrim(curbuf
->ot
+ curot
, prim
);
436 /* TODO: this works but TPAGE management is fucked
437 which makes it botch some tiles
438 if (pic->tpage != curtpage) {
440 if (drawhack && curot < OT_LEN-1) ++curot;
441 tpage = (DR_TPAGE *)primptr;
442 primptr += sizeof(DR_TPAGE);
443 curtpage = pic->tpage;
444 setDrawTPage(tpage, 1, 0, curtpage);
445 addPrim(curbuf->ot + curot, tpage);
446 if (curot < OT_LEN-1) ++curot;
449 if (w == 8 && h == 8) {
450 SPRT_8 *prim = (SPRT_8 *)primptr;
451 primptr += sizeof(SPRT_8);
453 setRGB0(prim, 128, 128, 128);
454 setXY0(prim, sx, sy);
455 setUV0(prim, pic->u, pic->v);
456 prim->clut = clut_id[0];
457 addPrim(curbuf->ot + curot, prim);
459 SPRT *prim = (SPRT *)primptr;
460 primptr += sizeof(SPRT);
462 setRGB0(prim, 128, 128, 128);
463 setWH(prim, pic->img->w, pic->img->h);
464 setXY0(prim, sx - pic->img->sx, sy - pic->img->sy);
465 setUV0(prim, pic->u, pic->v);
466 prim->clut = clut_id[0];
467 addPrim(curbuf->ot + curot, prim);
474 void Z_drawfld(byte
* fld
, int bg
) {
475 static const wrgb
[3][3] = { {0, 0, 255}, {0, 255, 0}, {255, 0, 0} };
477 int x
, y
, camx1
, camy1
, camx2
, camy2
;
482 hh
= (HT
>> 1) + 1 + w_o
;
483 camx1
= (w_x
- (WD
>> 1)) >> 3;
484 camx2
= camx1
+ (WD
>> 3) + 1;
485 camy1
= (w_y
- (HT
>> 1)) >> 3;
486 camy2
= camy1
+ (HT
>> 3) + 2;
488 if ((camx1
-= 4) < 0) camx1
= 0;
489 if ((camy1
-= 7) < 0) camy1
= 0;
490 if (camx2
> FLDW
) camx2
= FLDW
;
491 if (camy2
> FLDH
) camy2
= FLDH
;
493 prow
= fld
+ camy1
* FLDW
+ camx1
;
494 for (y
= camy1
; y
< camy2
; ++y
, prow
+= FLDW
) {
496 for (x
= camx1
; x
< camx2
; ++x
, ++p
) {
497 sx
= (x
<< 3) - w_x
+ hw
;
498 sy
= (y
<< 3) - w_y
+ hh
;
501 if ((u_long
)pic
<= 3) {
502 int i
= (u_long
)pic
- 1;
503 TILE_8
*prim
= (TILE_8
*)primptr
; primptr
+= sizeof(TILE_8
);
505 setXY0(prim
, sx
, sy
);
506 setRGB0(prim
, wrgb
[i
][0], wrgb
[i
][1], wrgb
[i
][2]);
507 setSemiTrans(prim
, 1);
508 addPrim(curbuf
->ot
+ curot
, prim
);
510 draw_wall(pic
, sx
, sy
);
516 if (curot
< OT_LEN
-1) ++curot
;
519 static void UnfuckLoadImage(RECT
*r
, vgaimg
*img
) {
520 // resize and load image that has odd width
521 // this shouldn't happen to any wallaby
527 u_char
*pin
= (u_char
*)img
+ sizeof(vgaimg
);
528 u_char
*buf
= malloc3(nw
* h
);
529 if (!buf
) ERR_fatal("UnfuckLoadImage(): out of memory\n");
530 for (pout
= buf
, i
= 0; i
< h
; ++i
, pin
+= w
, pout
+= nw
) {
531 memcpy(pout
, pin
, w
);
532 *(pout
+ w
) = 0; // fill extra column with transparency (or black)
534 LoadImage(r
, (u_long
*)buf
);
538 void V_fitpic(cacheimg
*cimg
) {
539 short besth
= CACHE_HEIGHT
;
545 ERR_fatal("V_fitpic(): NULL image");
547 // since our images are 8bpp, 2 pixels fit into one vram cell
548 // so divide w by 2 rounding up, and also round h up to 2
551 r
.w
= (cimg
->img
->w
+ 1) >> 1;
555 printf("V_fitpic(%dx%d): WARNING, > 256px width!\n", cimg
->img
->w
, cimg
->img
->h
);
557 for (i
= 0; i
< CACHE_WIDTH
- r
.w
; ++i
) {
561 if (vram_alloc
[i
+ j
] >= besth
)
563 if (vram_alloc
[i
+ j
] > th
)
564 th
= vram_alloc
[i
+ j
];
574 if (besth
+ r
.h
> CACHE_HEIGHT
)
575 ERR_fatal("V_fitpic(%dx%d): VRAM atlas full!\n", (int)cimg
->img
->w
, (int)cimg
->img
->h
);
577 if (r
.y
< 256 && r
.y
+ r
.h
> 256) {
578 besth
= 256; // crossing 256 pixel boundary, snap to the next 256
582 for (i
= 0; i
< r
.w
; ++i
)
583 vram_alloc
[r
.x
+ i
] = besth
+ r
.h
;
587 if (cimg
->img
->w
& 1)
588 UnfuckLoadImage(&r
, cimg
->img
);
590 LoadImage(&r
, (u_long
*)((u_char
*)cimg
->img
+ sizeof(vgaimg
)));
594 cimg
->tpage
= getTPage(1, 0, vrx
, vry
);
595 cimg
->u
= ((r
.x
- vrx
) << 1) & 0xFF;
596 cimg
->v
= r
.y
& 0xFF;
599 void V_clearmem(void) {
601 memset(vram_alloc
, 0, sizeof(vram_alloc
));
602 for (i
= 0; i
< MAX_CACHEDPICS
; ++i
)
603 piccache
[i
].tpage
= 0xFFFF;
606 cacheimg
*V_cachepic(vgaimg
*img
) {
608 if (!img
) return NULL
;
609 for (i
= 0; i
< MAX_CACHEDPICS
; ++i
) {
610 if (piccache
[i
].img
== img
)
612 if (!piccache
[i
].img
) {
613 piccache
[i
].img
= img
;
614 piccache
[i
].tpage
= 0xFFFF;
618 ERR_fatal("V_cachepic(): ran out of cache space!\n");
621 void V_uncachepic(cacheimg
*cimg
) {
625 cimg
->tpage
= 0xFFFF;
628 void V_startframe(void) {
630 curbuf
= dbuf
+ bufidx
;
631 ClearOTag(curbuf
->ot
, OT_LEN
);
633 primptr
= curbuf
->gpudata
;
637 void V_endframe(void) {
640 PutDispEnv(&curbuf
->dispenv
);
641 PutDrawEnv(&curbuf
->drawenv
);
642 DrawOTag(curbuf
->ot
);
645 void Z_loadingscreen(unsigned int x
) {
647 // finish all rendering
650 PutDispEnv(&curbuf
->dispenv
);
651 PutDrawEnv(&curbuf
->drawenv
);
657 // ClearImage(&r, 0, 0, 0);
658 // and now just yeet the pic into framebuffer
663 LoadImage2(&r
, (u_long
*)loadimg
);
666 void Z_clearscreen(int rr
, int g
, int b
) {
668 // finish all rendering
671 PutDispEnv(&curbuf
->dispenv
);
672 PutDrawEnv(&curbuf
->drawenv
);
677 ClearImage(&r
, rr
, g
, b
);