1 /*
2 * Glide64 - Glide video plugin for Nintendo 64 emulators.
3 * Copyright (c) 2002  Dave2001
4 * Copyright (c) 2003-2009  Sergey 'Gonetz' Lipski
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 //****************************************************************
22 //
23 // Glide64 - Glide Plugin for Nintendo 64 emulators
24 // Project started on December 29th, 2001
25 //
26 // Authors:
27 // Dave2001, original author, founded the project in 2001, left it in 2002
28 // Gugaman, joined the project in 2002, left it in 2002
29 // Sergey 'Gonetz' Lipski, joined the project in 2002, main author since fall of 2002
30 // Hiroshi 'KoolSmoky' Morii, joined the project in 2007
31 //
32 //****************************************************************
33 //
34 // To modify Glide64:
35 // * Write your name and (optional)email, commented by your work, so I know who did it, and so that you can find which parts you modified when it comes time to send it to me.
36 // * Do NOT send me the whole project or file that you modified->  Take out your modified code sections, and tell me where to put them.  If people sent the whole thing, I would have many different versions, but no idea how to combine them all.
37 //
38 //****************************************************************
39 
40 #include "../../Graphics/HLE/Microcode/S2DEX.h"
41 #include "../../Graphics/image_convert.h"
42 
43 
44 typedef struct DRAWIMAGE_t
45 {
46   float frameX;
47   float frameY;
48   uint16_t frameW;
49   uint16_t frameH;
50   uint16_t imageX;
51   uint16_t imageY;
52   uint16_t imageW;
53   uint16_t imageH;
54   uint32_t imagePtr;
55   uint8_t imageFmt;
56   uint8_t imageSiz;
57   uint16_t imagePal;
58   uint8_t flipX;
59   uint8_t flipY;
60   float scaleX;
61   float scaleY;
62 } DRAWIMAGE;
63 
DrawDepthImage(const DRAWIMAGE * d)64 static void DrawDepthImage (const DRAWIMAGE *d)
65 {
66    float scale_x_src, scale_y_src, scale_x_dst, scale_y_dst;
67    uint16_t *src, *dst;
68    int32_t x, y, src_width, src_height, dst_width, dst_height;
69 
70    if (!fb_depth_render_enabled || d->imageH > d->imageW)
71       return;
72 
73    scale_x_dst = rdp.scale_x;
74    scale_y_dst = rdp.scale_y;
75    scale_x_src = 1.0f/rdp.scale_x;
76    scale_y_src = 1.0f/rdp.scale_y;
77    src_width   = d->imageW;
78    src_height  = d->imageH;
79    dst_width   = MIN((int)(src_width*scale_x_dst), (int)settings.scr_res_x);
80    dst_height  = MIN((int)(src_height*scale_y_dst), (int)settings.scr_res_y);
81    src         = (uint16_t*)(gfx_info.RDRAM+d->imagePtr);
82    dst         = (uint16_t*)malloc(dst_width * dst_height * sizeof(uint16_t));
83 
84    for (y = 0; y < dst_height; y++)
85    {
86       for (x = 0; x < dst_width; x++)
87          dst[x + y * dst_width] = src[((int)(x * scale_x_src) + (int)(y*scale_y_src) * src_width)^1];
88    }
89    grLfbWriteRegion(GR_BUFFER_AUXBUFFER,
90          0,
91          0,
92          GR_LFB_SRC_FMT_ZA16,
93          dst_width,
94          dst_height,
95          FXFALSE,
96          dst_width<<1,
97          dst);
98    free(dst);
99 }
100 
DrawImage(DRAWIMAGE * d)101 static void DrawImage (DRAWIMAGE *d)
102 {
103    int x_size, y_size, x_shift, y_shift, line;
104    int min_wrap_u, min_wrap_v, min_256_u, min_256_v;
105    float nul_y, nlr_x;
106    int nul_v, nlr_u;
107    int cur_wrap_v, cur_v;
108    int cb_v; // coordinate-base
109    int tb_v; // texture-base
110    float ful_u, ful_v, flr_u, flr_v;
111    float ful_x, ful_y, flr_x, flr_y, mx, bx, my, by;
112    float Z;
113    struct gDPTile *tile;
114    int ul_u, ul_v, lr_u, lr_v;
115    float ul_x, ul_y, lr_x, lr_y;
116 
117    if (d->imageW == 0 || d->imageH == 0 || d->frameH == 0)
118       return;
119 
120    // choose optimum size for the format/size
121    switch (d->imageSiz)
122    {
123       case 0:
124          y_size  = 32;
125          y_shift = 5;
126          if (rdp.tlut_mode < G_TT_RGBA16)
127          {
128             y_size  = 64;
129             y_shift = 6;
130          }
131          x_size  = 128;
132          x_shift = 7;
133          line    = 8;
134          break;
135       case 1:
136          y_size  = 32;
137          y_shift = 5;
138          if (rdp.tlut_mode < G_TT_RGBA16)
139          {
140             y_size  = 64;
141             y_shift = 6;
142          }
143          x_size  = 64;
144          x_shift = 6;
145          line    = 8;
146          break;
147       case 2:
148          x_size  = 64;
149          y_size  = 32;
150          x_shift = 6;
151          y_shift = 5;
152          line    = 16;
153          break;
154       case 3:
155          x_size  = 32;
156          y_size  = 16;
157          x_shift = 4;
158          y_shift = 3;
159          line    = 16;
160          break;
161       default:
162          FRDP("DrawImage. unknown image size: %d\n", d->imageSiz);
163          return;
164    }
165 
166    if (gDP.colorImage.width == 512 && !no_dlist) //RE2
167    {
168       uint16_t width = (uint16_t)(*gfx_info.VI_WIDTH_REG & 0xFFF);
169       d->frameH      = d->imageH = (d->frameW * d->frameH)/width;
170       d->frameW      = d->imageW = width;
171 
172       if (g_gdp.zb_address == gDP.colorImage.address)
173       {
174          DrawDepthImage(d);
175          g_gdp.flags |= UPDATE_ZBUF_ENABLED | UPDATE_COMBINE |
176             UPDATE_ALPHA_COMPARE | UPDATE_VIEWPORT;
177          return;
178       }
179    }
180 
181    if ((settings.hacks&hack_PPL) > 0)
182    {
183       if (d->imageY > d->imageH)
184          d->imageY = (d->imageY % d->imageH);
185    }
186    else
187    if ((settings.hacks&hack_Starcraft) > 0)
188    {
189       if (d->imageH%2 == 1)
190          d->imageH -= 1;
191    }
192    else
193    {
194       if ( (d->frameX > 0) && (d->frameW == gDP.colorImage.width) )
195          d->frameW -= (uint16_t)(2.0f*d->frameX);
196       if ( (d->frameY > 0) && (d->frameH == gDP.colorImage.height) )
197          d->frameH -= (uint16_t)(2.0f*d->frameY);
198    }
199 
200 
201    ul_u = (int)d->imageX;
202    ul_v = (int)d->imageY;
203    lr_u = (int)d->imageX + (int)(d->frameW * d->scaleX);
204    lr_v = (int)d->imageY + (int)(d->frameH * d->scaleY);
205 
206    ul_x = d->frameX;
207    lr_x = d->frameX + d->frameW;
208    ul_y = d->frameY;
209    lr_y = d->frameY + d->frameH;
210 
211    if (d->flipX)
212    {
213       ul_x = d->frameX + d->frameW;
214       lr_x = d->frameX;
215    }
216    if (d->flipY)
217    {
218       ul_y = d->frameY + d->frameH;
219       lr_y = d->frameY;
220    }
221 
222    min_wrap_u = ul_u / d->imageW;
223    min_wrap_v = ul_v / d->imageH;
224    min_256_u  = ul_u >> x_shift;
225    min_256_v  = ul_v >> y_shift;
226 
227    glide64gDPSetTextureImage(
228          d->imageFmt,                              /* format - RGBA */
229          d->imageSiz,                              /* size   - 16bit */
230          (d->imageW%2)?d->imageW-1:d->imageW,      /* width */
231          d->imagePtr                               /* address */
232          );
233 
234    rdp.timg.set_by        = 0;
235 
236    glide64gDPSetTile(
237          d->imageFmt,               /* RGBA */
238          d->imageSiz,               /* 16bit */
239          line,                      /* line */
240          0,                         /* tmem */
241          0,                         /* tile */
242          (uint8_t)d->imagePal,      /* palette */
243          0,                         /* cmt */
244          0,                         /* mask_t */
245          0,                         /* shift_t */
246          0,                         /* cms */
247          0,                         /* mask_s */
248          0                          /* shift_s */
249          );
250 
251    glide64gDPSetTileSize(
252          0,                /* tile */
253          0,                /* ulx  */
254          0,                /* uly  */
255          x_size - 1,       /* lrs  */
256          y_size - 1        /* lrt  */
257          );
258 
259    Z = set_sprite_combine_mode ();
260    if (((gDP.otherMode.h & RDP_CYCLE_TYPE) >> 20) == G_CYC_COPY)
261       rdp.allow_combine = 0;
262 
263    {
264       uint32_t minx = 0;
265       uint32_t miny = 0;
266       uint32_t maxx, maxy;
267       if (gDP.colorImage.width == 512 && !no_dlist)
268       {
269          maxx = settings.scr_res_x;
270          maxy = settings.scr_res_y;
271       }
272       else if (d->scaleX == 1.0f && d->scaleY == 1.0f)
273       {
274          minx = rdp.scissor.ul_x;
275          miny = rdp.scissor.ul_y;
276          maxx = rdp.scissor.lr_x;
277          maxy = rdp.scissor.lr_y;
278       }
279       else
280       {
281          minx = rdp.scissor.ul_x;
282          miny = rdp.scissor.ul_y;
283          maxx = MIN(rdp.scissor.lr_x, (uint32_t)((d->frameX+d->imageW/d->scaleX+0.5f)*rdp.scale_x));
284          maxy = MIN(rdp.scissor.lr_y, (uint32_t)((d->frameY+d->imageH/d->scaleY+0.5f)*rdp.scale_y));
285       }
286       grClipWindow(minx, miny, maxx, maxy);
287       g_gdp.flags |= UPDATE_SCISSOR;
288    }
289 
290    // Texture ()
291    rdp.cur_tile = 0;
292 
293    mx    = (float)(lr_x - ul_x) / (float)(lr_u - ul_u);
294    bx    = ul_x - mx * ul_u;
295 
296    my    = (float)(lr_y - ul_y) / (float)(lr_v - ul_v);
297    by    = ul_y - my * ul_v;
298 
299 
300    nul_v = ul_v;
301    nul_y = ul_y;
302 
303    // #162
304 
305    cur_wrap_v = min_wrap_v + 1;
306    cur_v      = min_256_v + 1;
307    cb_v       = ((cur_v-1)<<y_shift);
308    while (cb_v >= d->imageH) cb_v -= d->imageH;
309    tb_v       = cb_v;
310    rdp.bg_image_height = d->imageH;
311 
312    while (1)
313    {
314       int nul_u, cb_u, tb_u;
315       float nul_x, nlr_y;
316       int cur_wrap_u = min_wrap_u + 1;
317       int cur_u      = min_256_u + 1;
318 
319       // calculate intersection with this point
320       int nlr_v      = MIN (MIN (cur_wrap_v*d->imageH, (cur_v<<y_shift)), lr_v);
321       nlr_y          = my * nlr_v + by;
322 
323       nul_u          = ul_u;
324       nul_x          = ul_x;
325       cb_u           = ((cur_u-1)<<x_shift);
326       while (cb_u >= d->imageW)
327          cb_u       -= d->imageW;
328       tb_u           = cb_u;
329 
330       while (1)
331       {
332          // calculate intersection with this point
333          nlr_u = MIN (MIN (cur_wrap_u * d->imageW, (cur_u<<x_shift)), lr_u);
334          nlr_x = mx * nlr_u + bx;
335 
336          // ** Load the texture, constant portions have been set above
337          glide64gDPSetTileSize(
338                0,                /* tile */
339                tb_u,             /* uls  */
340                tb_v,             /* ult  */
341                tb_u+x_size-1,    /* lrs  */
342                tb_v+y_size-1     /* lrt  */
343                );
344 
345          __RSP.w0 = ((int)g_gdp.tile[0].sh << 14) | ((int)g_gdp.tile[0].th << 2);
346          __RSP.w1 = ((int)g_gdp.tile[0].sl << 14) | ((int)g_gdp.tile[0].tl << 2);
347          glide64gDPLoadTile(
348                (uint32_t)((__RSP.w1 >> 24) & 0x07),      /* tile */
349                (uint32_t)((__RSP.w0 >> 14) & 0x03FF),    /* ul_s */
350                (uint32_t)((__RSP.w0 >> 2 ) & 0x03FF),    /* ul_t */
351                (uint32_t)((__RSP.w1 >> 14) & 0x03FF),    /* lr_s */
352                (uint32_t)((__RSP.w1 >> 2 ) & 0x03FF)     /* lr_t */
353                );
354          TexCache ();
355 
356          ful_u = (float)nul_u - cb_u;
357          flr_u = (float)nlr_u - cb_u;
358          ful_v = (float)nul_v - cb_v;
359          flr_v = (float)nlr_v - cb_v;
360 
361          ful_u *= rdp.cur_cache[0]->c_scl_x;
362          ful_v *= rdp.cur_cache[0]->c_scl_y;
363          flr_u *= rdp.cur_cache[0]->c_scl_x;
364          flr_v *= rdp.cur_cache[0]->c_scl_y;
365 
366          ful_x = nul_x * rdp.scale_x + rdp.offset_x;
367          flr_x = nlr_x * rdp.scale_x + rdp.offset_x;
368          ful_y = nul_y * rdp.scale_y + rdp.offset_y;
369          flr_y = nlr_y * rdp.scale_y + rdp.offset_y;
370 
371          /* Make the vertices */
372 
373          if ((flr_x <= rdp.scissor.lr_x) || (ful_x < rdp.scissor.lr_x))
374          {
375             VERTEX v[4];
376 
377             v[0].x = ful_x;
378             v[0].y = ful_y;
379             v[0].z = Z;
380             v[0].q = 1.0f;
381             v[0].u[0] = ful_u;
382             v[0].v[0] = ful_v;
383 
384             v[1].x = flr_x;
385             v[1].y = ful_y;
386             v[1].z = Z;
387             v[1].q = 1.0f;
388             v[1].u[0] = flr_u;
389             v[1].v[0] = ful_v;
390 
391             v[2].x = ful_x;
392             v[2].y = flr_y;
393             v[2].z = Z;
394             v[2].q = 1.0f;
395             v[2].u[0] = ful_u;
396             v[2].v[0] = flr_v;
397 
398             v[3].x = flr_x;
399             v[3].y = flr_y;
400             v[3].z = Z;
401             v[3].q = 1.0f;
402             v[3].u[0] = flr_u;
403             v[3].v[0] = flr_v;
404 
405             apply_shading(v);
406             ConvertCoordsConvert (v, 4);
407             grDrawVertexArrayContiguous (GR_TRIANGLE_STRIP, 4, v);
408          }
409 
410          // increment whatever caused this split
411          tb_u += x_size - (x_size-(nlr_u-cb_u));
412          cb_u  = nlr_u;
413          if (nlr_u == cur_wrap_u * d->imageW)
414          {
415             cur_wrap_u ++;
416             tb_u = 0;
417          }
418          if (nlr_u == (cur_u<<x_shift))
419             cur_u ++;
420          if (nlr_u == lr_u)
421             break;
422          nul_u = nlr_u;
423          nul_x = nlr_x;
424       }
425 
426       tb_v += y_size - (y_size-(nlr_v-cb_v));
427       cb_v = nlr_v;
428       if (nlr_v == cur_wrap_v* d->imageH)
429       {
430          cur_wrap_v ++;
431          tb_v = 0;
432       }
433       if (nlr_v == (cur_v<<y_shift))
434          cur_v ++;
435       if (nlr_v == lr_v)
436          break;
437       nul_v = nlr_v;
438       nul_y = nlr_y;
439    }
440 
441    rdp.allow_combine = 1;
442    rdp.bg_image_height = 0xFFFF;
443 }
444 
445 //****************************************************************
446 
uc6_read_background_data(DRAWIMAGE * d,bool bReadScale)447 static void uc6_read_background_data (DRAWIMAGE *d, bool bReadScale)
448 {
449    int imageYorig;
450    uint16_t imageFlip;
451    uint32_t addr  = RSP_SegmentToPhysical(__RSP.w1) >> 1;
452 
453    d->imageX      = (((uint16_t *)gfx_info.RDRAM)[(addr+0)^1] >> 5);   // 0
454    d->imageW      = (((uint16_t *)gfx_info.RDRAM)[(addr+1)^1] >> 2);   // 1
455    d->frameX      = ((int16_t*)gfx_info.RDRAM)[(addr+2)^1] / 4.0f;       // 2
456    d->frameW      = ((uint16_t *)gfx_info.RDRAM)[(addr+3)^1] >> 2;             // 3
457 
458    d->imageY      = (((uint16_t *)gfx_info.RDRAM)[(addr+4)^1] >> 5);   // 4
459    d->imageH      = (((uint16_t *)gfx_info.RDRAM)[(addr+5)^1] >> 2);   // 5
460    d->frameY      = ((int16_t*)gfx_info.RDRAM)[(addr+6)^1] / 4.0f;       // 6
461    d->frameH      = ((uint16_t *)gfx_info.RDRAM)[(addr+7)^1] >> 2;             // 7
462 
463    d->imagePtr    = RSP_SegmentToPhysical(((uint32_t*)gfx_info.RDRAM)[(addr+8)>>1]);       // 8,9
464    d->imageFmt    = ((uint8_t *)gfx_info.RDRAM)[(((addr+11)<<1)+0)^3]; // 11
465    d->imageSiz    = ((uint8_t *)gfx_info.RDRAM)[(((addr+11)<<1)+1)^3]; // |
466    d->imagePal    = ((uint16_t *)gfx_info.RDRAM)[(addr+12)^1]; // 12
467    imageFlip      = ((uint16_t *)gfx_info.RDRAM)[(addr+13)^1];    // 13;
468    d->flipX       = (uint8_t)imageFlip & G_BG_FLAG_FLIPS;
469 
470    if (bReadScale)
471    {
472       d->scaleX   = ((int16_t*)gfx_info.RDRAM)[(addr+14)^1] / 1024.0f;  // 14
473       d->scaleY   = ((int16_t*)gfx_info.RDRAM)[(addr+15)^1] / 1024.0f;  // 15
474    }
475    else
476       d->scaleX   = d->scaleY = 1.0f;
477 
478    d->flipY       = 0;
479    imageYorig     = ((int *)gfx_info.RDRAM)[(addr+16)>>1] >> 5;
480    rdp.last_bg    = d->imagePtr;
481 }
482 
uc6_bg(bool first_cycle)483 static void uc6_bg(bool first_cycle)
484 {
485    DRAWIMAGE d;
486 
487    if (rdp.skip_drawing)
488       return;
489 
490    uc6_read_background_data(&d, first_cycle);
491 
492    if (settings.ucode == ucode_F3DEX2 || (settings.hacks&hack_PPL))
493    {
494       /* can't draw from framebuffer */
495       if (d.imagePtr == gDP.colorImage.address || d.imagePtr == rdp.ocimg)
496          return;
497       if (!d.imagePtr)
498          return;
499    }
500 
501    DrawImage(&d);
502 }
503 
uc6_bg_1cyc(uint32_t w0,uint32_t w1)504 static void uc6_bg_1cyc(uint32_t w0, uint32_t w1)
505 {
506    uc6_bg(true);
507 }
508 
uc6_bg_copy(uint32_t w0,uint32_t w1)509 static void uc6_bg_copy(uint32_t w0, uint32_t w1)
510 {
511    uc6_bg(false);
512 }
513 
draw_split_triangle(VERTEX ** vtx)514 static void draw_split_triangle(VERTEX **vtx)
515 {
516   int index,i,j, min_256,max_256, cur_256;
517   float percent;
518 
519   vtx[0]->not_zclipped = 1;
520   vtx[1]->not_zclipped = 1;
521   vtx[2]->not_zclipped = 1;
522 
523   min_256 = MIN((int)vtx[0]->u[0],(int)vtx[1]->u[0]); // bah, don't put two mins on one line
524   min_256 = MIN(min_256,(int)vtx[2]->u[0]) >> 8;  // or it will be calculated twice
525 
526   max_256 = MAX((int)vtx[0]->u[0],(int)vtx[1]->u[0]); // not like it makes much difference
527   max_256 = MAX(max_256,(int)vtx[2]->u[0]) >> 8;  // anyway :P
528 
529   for (cur_256=min_256; cur_256<=max_256; cur_256++)
530   {
531     int left_256 = cur_256 << 8;
532     int right_256 = (cur_256+1) << 8;
533 
534     // Set vertex buffers
535     rdp.vtxbuf = rdp.vtx1;  // copy from v to rdp.vtx1
536     rdp.vtxbuf2 = rdp.vtx2;
537     rdp.vtx_buffer = 0;
538     rdp.n_global   = 3;
539     index          = 0;
540 
541     // ** Left plane **
542     for (i=0; i<3; i++)
543     {
544        VERTEX *v2 = NULL;
545        VERTEX *v1 = (VERTEX*)vtx[i];
546 
547        j = i+1;
548        if (j == 3)
549           j = 0;
550 
551        v2 = (VERTEX*)vtx[j];
552 
553        if (v1->u[0] >= left_256)
554        {
555           if (v2->u[0] >= left_256)   // Both are in, save the last one
556           {
557              rdp.vtxbuf[index]         = *v2;
558              rdp.vtxbuf[index].u[0]   -= left_256;
559              rdp.vtxbuf[index++].v[0] += rdp.cur_cache[0]->c_scl_y * (cur_256 * rdp.cur_cache[0]->splitheight);
560           }
561           else      // First is in, second is out, save intersection
562           {
563              percent = (left_256 - v1->u[0]) / (v2->u[0] - v1->u[0]);
564              rdp.vtxbuf[index].x    = v1->x + (v2->x - v1->x) * percent;
565              rdp.vtxbuf[index].y    = v1->y + (v2->y - v1->y) * percent;
566              rdp.vtxbuf[index].z    = 1;
567              rdp.vtxbuf[index].q    = 1;
568              rdp.vtxbuf[index].u[0] = 0.5f;
569              rdp.vtxbuf[index].v[0] = v1->v[0] + (v2->v[0] - v1->v[0]) * percent +
570                 rdp.cur_cache[0]->c_scl_y * cur_256 * rdp.cur_cache[0]->splitheight;
571              rdp.vtxbuf[index].b    = (uint8_t)(v1->b + (v2->b - v1->b) * percent);
572              rdp.vtxbuf[index].g    = (uint8_t)(v1->g + (v2->g - v1->g) * percent);
573              rdp.vtxbuf[index].r    = (uint8_t)(v1->r + (v2->r - v1->r) * percent);
574              rdp.vtxbuf[index++].a  = (uint8_t)(v1->a + (v2->a - v1->a) * percent);
575           }
576        }
577        else
578        {
579           if (v2->u[0] >= left_256) // First is out, second is in, save intersection & in point
580           {
581              percent = (left_256 - v2->u[0]) / (v1->u[0] - v2->u[0]);
582              rdp.vtxbuf[index].x = v2->x + (v1->x - v2->x) * percent;
583              rdp.vtxbuf[index].y = v2->y + (v1->y - v2->y) * percent;
584              rdp.vtxbuf[index].z = 1;
585              rdp.vtxbuf[index].q = 1;
586              rdp.vtxbuf[index].u[0] = 0.5f;
587              rdp.vtxbuf[index].v[0] = v2->v[0] + (v1->v[0] - v2->v[0]) * percent +
588                 rdp.cur_cache[0]->c_scl_y * cur_256 * rdp.cur_cache[0]->splitheight;
589              rdp.vtxbuf[index].b = (uint8_t)(v2->b + (v1->b - v2->b) * percent);
590              rdp.vtxbuf[index].g = (uint8_t)(v2->g + (v1->g - v2->g) * percent);
591              rdp.vtxbuf[index].r = (uint8_t)(v2->r + (v1->r - v2->r) * percent);
592              rdp.vtxbuf[index++].a = (uint8_t)(v2->a + (v1->a - v2->a) * percent);
593 
594              // Save the in point
595              rdp.vtxbuf[index] = *v2;
596              rdp.vtxbuf[index].u[0] -= left_256;
597              rdp.vtxbuf[index++].v[0] += rdp.cur_cache[0]->c_scl_y * (cur_256 * rdp.cur_cache[0]->splitheight);
598           }
599        }
600     }
601     rdp.n_global = index;
602 
603     rdp.vtxbuf = rdp.vtx2;  // now vtx1 holds the value, & vtx2 is the destination
604     rdp.vtxbuf2 = rdp.vtx1;
605     rdp.vtx_buffer ^= 1;
606     index = 0;
607 
608     for (i=0; i<rdp.n_global; i++)
609     {
610        VERTEX *v1 = (VERTEX*)&rdp.vtxbuf2[i];
611        VERTEX *v2 = NULL;
612 
613        j = i+1;
614        if (j == rdp.n_global)
615           j = 0;
616 
617        v2 = (VERTEX*)&rdp.vtxbuf2[j];
618 
619        // ** Right plane **
620        if (v1->u[0] <= 256.0f)
621        {
622           if (v2->u[0] <= 256.0f)   // Both are in, save the last one
623           {
624              rdp.vtxbuf[index++] = *v2;
625           }
626           else      // First is in, second is out, save intersection
627           {
628              percent = (right_256 - v1->u[0]) / (v2->u[0] - v1->u[0]);
629              rdp.vtxbuf[index].x = v1->x + (v2->x - v1->x) * percent;
630              rdp.vtxbuf[index].y = v1->y + (v2->y - v1->y) * percent;
631              rdp.vtxbuf[index].z = 1;
632              rdp.vtxbuf[index].q = 1;
633              rdp.vtxbuf[index].u[0] = 255.5f;
634              rdp.vtxbuf[index].v[0] = v1->v[0] + (v2->v[0] - v1->v[0]) * percent;
635              rdp.vtxbuf[index].b = (uint8_t)(v1->b + (v2->b - v1->b) * percent);
636              rdp.vtxbuf[index].g = (uint8_t)(v1->g + (v2->g - v1->g) * percent);
637              rdp.vtxbuf[index].r = (uint8_t)(v1->r + (v2->r - v1->r) * percent);
638              rdp.vtxbuf[index++].a = (uint8_t)(v1->a + (v2->a - v1->a) * percent);
639           }
640        }
641        else
642        {
643           if (v2->u[0] <= 256.0f) // First is out, second is in, save intersection & in point
644           {
645              percent = (right_256 - v2->u[0]) / (v1->u[0] - v2->u[0]);
646              rdp.vtxbuf[index].x = v2->x + (v1->x - v2->x) * percent;
647              rdp.vtxbuf[index].y = v2->y + (v1->y - v2->y) * percent;
648              rdp.vtxbuf[index].z = 1;
649              rdp.vtxbuf[index].q = 1;
650              rdp.vtxbuf[index].u[0] = 255.5f;
651              rdp.vtxbuf[index].v[0] = v2->v[0] + (v1->v[0] - v2->v[0]) * percent;
652              rdp.vtxbuf[index].b = (uint8_t)(v2->b + (v1->b - v2->b) * percent);
653              rdp.vtxbuf[index].g = (uint8_t)(v2->g + (v1->g - v2->g) * percent);
654              rdp.vtxbuf[index].r = (uint8_t)(v2->r + (v1->r - v2->r) * percent);
655              rdp.vtxbuf[index++].a = (uint8_t)(v2->a + (v1->a - v2->a) * percent);
656 
657              // Save the in point
658              rdp.vtxbuf[index++] = *v2;
659           }
660        }
661     }
662     rdp.n_global = index;
663 
664     do_triangle_stuff_2 (0, 1, 1);
665   }
666 }
667 
uc6_draw_polygons(VERTEX v[4])668 static void uc6_draw_polygons (VERTEX v[4])
669 {
670    apply_shading(v);
671 
672    {
673       rdp.vtxbuf = rdp.vtx1; // copy from v to rdp.vtx1
674       rdp.vtxbuf2 = rdp.vtx2;
675       rdp.vtx_buffer = 0;
676       rdp.n_global = 3;
677       memcpy (rdp.vtxbuf, v, sizeof(VERTEX)*3);
678       do_triangle_stuff_2 (0, 1, 1);
679 
680       rdp.vtxbuf = rdp.vtx1; // copy from v to rdp.vtx1
681       rdp.vtxbuf2 = rdp.vtx2;
682       rdp.vtx_buffer = 0;
683       rdp.n_global = 3;
684       memcpy (rdp.vtxbuf, v+1, sizeof(VERTEX)*3);
685       do_triangle_stuff_2 (0, 1, 1);
686    }
687    g_gdp.flags |= UPDATE_ZBUF_ENABLED | UPDATE_VIEWPORT;
688 
689    if (settings.fog && (rdp.flags & FOG_ENABLED))
690       grFogMode (GR_FOG_WITH_TABLE_ON_FOGCOORD_EXT, g_gdp.fog_color.total);
691 }
692 
uc6_read_object_data(DRAWOBJECT * d)693 static void uc6_read_object_data (DRAWOBJECT *d)
694 {
695    uint32_t addr  = RSP_SegmentToPhysical(__RSP.w1) >> 1;
696 
697    d->objX        = ((int16_t*)gfx_info.RDRAM)[(addr+0)^1] / 4.0f;               // 0
698    d->scaleW      = ((uint16_t *)gfx_info.RDRAM)[(addr+1)^1] / 1024.0f;        // 1
699    d->imageW      = ((int16_t*)gfx_info.RDRAM)[(addr+2)^1] >> 5;                 // 2, 3 is padding
700    d->objY        = ((int16_t*)gfx_info.RDRAM)[(addr+4)^1] / 4.0f;               // 4
701    d->scaleH      = ((uint16_t *)gfx_info.RDRAM)[(addr+5)^1] / 1024.0f;        // 5
702    d->imageH      = ((int16_t*)gfx_info.RDRAM)[(addr+6)^1] >> 5;                 // 6, 7 is padding
703 
704    d->imageStride = ((uint16_t *)gfx_info.RDRAM)[(addr+8)^1];                  // 8
705    d->imageAdrs   = ((uint16_t *)gfx_info.RDRAM)[(addr+9)^1];                  // 9
706    d->imageFmt    = ((uint8_t *)gfx_info.RDRAM)[(((addr+10)<<1)+0)^3]; // 10
707    d->imageSiz    = ((uint8_t *)gfx_info.RDRAM)[(((addr+10)<<1)+1)^3]; // |
708    d->imagePal    = ((uint8_t *)gfx_info.RDRAM)[(((addr+10)<<1)+2)^3]; // 11
709    d->imageFlags  = ((uint8_t *)gfx_info.RDRAM)[(((addr+10)<<1)+3)^3]; // |
710 
711    if (d->imageW < 0)
712       d->imageW = (int16_t)g_gdp.__clip.xl - (int16_t)d->objX - d->imageW;
713    if (d->imageH < 0)
714       d->imageH = (int16_t)g_gdp.__clip.yl - (int16_t)d->objY - d->imageH;
715 }
716 
uc6_init_tile(const DRAWOBJECT * d)717 static void uc6_init_tile(const DRAWOBJECT *d)
718 {
719    glide64gDPSetTile(
720          d->imageFmt,       /* fmt - RGBA */
721          d->imageSiz,       /* siz - 16bit */
722          d->imageStride,    /* line */
723          d->imageAdrs,      /* tmem */
724          0,                 /* tile */
725          d->imagePal,       /* palette */
726          0,                 /* cmt */
727          0,                 /* maskt */
728          0,                 /* shift_t */
729          0,                 /* cms */
730          0,                 /* mask_s */
731          0                  /* shift_s */
732          );
733 
734    glide64gDPSetTileSize(
735          0,                                  /* tile */
736          0,                                  /* uls  */
737          0,                                  /* ult  */
738          (d->imageW>0) ? (d->imageW-1) : 0,  /* lrs */
739          (d->imageH>0) ? (d->imageH-1) : 0   /* lrt */
740          );
741 }
742 
uc6_obj_rectangle(uint32_t w0,uint32_t w1)743 static void uc6_obj_rectangle(uint32_t w0, uint32_t w1)
744 {
745    int i;
746    float Z, ul_x, lr_x, ul_y, lr_y, ul_u, ul_v, lr_u, lr_v;
747    VERTEX v[4];
748    DRAWOBJECT d;
749 
750    uc6_read_object_data(&d);
751 
752    if (d.imageAdrs > 4096)
753    {
754       FRDP("tmem: %08lx is out of bounds! return\n", d.imageAdrs);
755       return;
756    }
757    if (!rdp.s2dex_tex_loaded)
758    {
759       LRDP("Texture was not loaded! return\n");
760       return;
761    }
762 
763    uc6_init_tile(&d);
764 
765    Z = set_sprite_combine_mode();
766 
767    ul_x = d.objX;
768    lr_x = d.objX + d.imageW / d.scaleW;
769    ul_y = d.objY;
770    lr_y = d.objY + d.imageH / d.scaleH;
771    lr_u = 255.0f*rdp.cur_cache[0]->scale_x;
772    lr_v = 255.0f*rdp.cur_cache[0]->scale_y;
773    ul_u = 0.5f;
774    ul_v = 0.5f;
775 
776    if (d.imageFlags & G_BG_FLAG_FLIPS) /* flipS */
777    {
778       ul_u = lr_u;
779       lr_u = 0.5f;
780    }
781 
782    if (d.imageFlags & G_BG_FLAG_FLIPT) //flipT
783    {
784       ul_v = lr_v;
785       lr_v = 0.5f;
786    }
787 
788    /* Make the vertices */
789 
790    v[0].x = ul_x;
791    v[0].y = ul_y;
792    v[0].z = Z;
793    v[0].q = 1.0f;
794    v[0].u[0] = ul_u;
795    v[0].v[0] = ul_v;
796 
797    v[1].x = lr_x;
798    v[1].y = ul_y;
799    v[1].z = Z;
800    v[1].q = 1.0f;
801    v[1].u[0] = lr_u;
802    v[1].v[0] = ul_v;
803 
804    v[2].x = ul_x;
805    v[2].y = lr_y;
806    v[2].z = Z;
807    v[2].q = 1.0f;
808    v[2].u[0] = ul_u;
809    v[2].v[0] = lr_v;
810 
811    v[3].x = lr_x;
812    v[3].y = lr_y;
813    v[3].z = Z;
814    v[3].q = 1.0f;
815    v[3].u[0] = lr_u;
816    v[3].v[0] = lr_v;
817 
818    for (i = 0; i < 4; i++)
819    {
820       v[i].x = (v[i].x * rdp.scale_x) + rdp.offset_x;
821       v[i].y = (v[i].y * rdp.scale_y) + rdp.offset_y;
822    }
823 
824    uc6_draw_polygons (v);
825 }
826 
uc6_obj_sprite(uint32_t w0,uint32_t w1)827 void uc6_obj_sprite(uint32_t w0, uint32_t w1)
828 {
829    DRAWOBJECT d;
830    VERTEX v[4];
831    int i;
832    float Z, ul_x, lr_x, ul_y, lr_y, ul_u, lr_u, ul_v, lr_v;
833 
834    uc6_read_object_data(&d);
835    uc6_init_tile(&d);
836 
837    Z = set_sprite_combine_mode ();
838 
839    ul_x = d.objX;
840    lr_x = d.objX + d.imageW/d.scaleW;
841    ul_y = d.objY;
842    lr_y = d.objY + d.imageH/d.scaleH;
843 
844 #if 0
845    if (rdp.cur_cache[0]->splits > 1)
846    {
847       lr_u = (float)(d.imageW-1);
848       lr_v = (float)(d.imageH-1);
849    }
850    else
851 #endif
852    {
853       lr_u = 255.0f*rdp.cur_cache[0]->scale_x;
854       lr_v = 255.0f*rdp.cur_cache[0]->scale_y;
855    }
856 
857    if (d.imageFlags&0x01) //flipS
858    {
859       ul_u = lr_u;
860       lr_u = 0.5f;
861    }
862    else
863       ul_u = 0.5f;
864    if (d.imageFlags&0x10) //flipT
865    {
866       ul_v = lr_v;
867       lr_v = 0.5f;
868    }
869    else
870       ul_v = 0.5f;
871 
872    /* Make the vertices */
873    // FRDP("scale_x: %f, scale_y: %f\n", rdp.cur_cache[0]->scale_x, rdp.cur_cache[0]->scale_y);
874 
875    v[0].x = ul_x;
876    v[0].y = ul_y;
877    v[0].z = Z;
878    v[0].q = 1.0f;
879    v[0].u[0] = ul_u;
880    v[0].v[0] = ul_v;
881 
882    v[1].x = lr_x;
883    v[1].y = ul_y;
884    v[1].z = Z;
885    v[1].q = 1.0f;
886    v[1].u[0] = lr_u;
887    v[1].v[0] = ul_v;
888 
889    v[2].x = ul_x;
890    v[2].y = lr_y;
891    v[2].z = Z;
892    v[2].q = 1.0f;
893    v[2].u[0] = ul_u;
894    v[2].v[0] = lr_v;
895 
896    v[3].x = lr_x;
897    v[3].y = lr_y;
898    v[3].z = Z;
899    v[3].q = 1.0f;
900    v[3].u[0] = lr_u;
901    v[3].v[0] = lr_v;
902 
903    for (i = 0; i < 4; i++)
904    {
905       float x = v[i].x;
906       float y = v[i].y;
907       v[i].x = ((x * gSP.objMatrix.A + y * gSP.objMatrix.B + gSP.objMatrix.X) * rdp.scale_x) + rdp.offset_x;
908       v[i].y = ((x * gSP.objMatrix.C + y * gSP.objMatrix.D + gSP.objMatrix.Y) * rdp.scale_y) + rdp.offset_y;
909    }
910 
911    uc6_draw_polygons (v);
912 }
913 
uc6_obj_movemem(uint32_t w0,uint32_t w1)914 static void uc6_obj_movemem(uint32_t w0, uint32_t w1)
915 {
916    switch (_SHIFTR( w0, 0, 16 ))
917    {
918       case S2DEX_MV_MATRIX:
919          glide64gSPObjMatrix( w1 );
920          break;
921 		case S2DEX_MV_SUBMATRIX:
922 			glide64gSPObjSubMatrix( w1 );
923 			break;
924    }
925 }
926 
uc6_select_dl(uint32_t w0,uint32_t w1)927 static void uc6_select_dl(uint32_t w0, uint32_t w1)
928 {
929 }
930 
uc6_obj_rendermode(uint32_t w0,uint32_t w1)931 static void uc6_obj_rendermode(uint32_t w0, uint32_t w1)
932 {
933 }
934 
DrawYUVImageToFrameBuffer(uint16_t ul_x,uint16_t ul_y,uint16_t lr_x,uint16_t lr_y)935 static void DrawYUVImageToFrameBuffer(uint16_t ul_x, uint16_t ul_y, uint16_t lr_x, uint16_t lr_y)
936 {
937    uint16_t h, w, *dst;
938    uint32_t width, height, *mb;
939 
940    uint32_t ci_width  = gDP.colorImage.width;
941    uint32_t ci_height = rdp.ci_lower_bound;
942 
943    if (ul_x >= ci_width)
944       return;
945    if (ul_y >= ci_height)
946       return;
947 
948    width  = 16;
949    height = 16;
950 
951    if (lr_x > ci_width)
952       width  = ci_width - ul_x;
953    if (lr_y > ci_height)
954       height = ci_height - ul_y;
955 
956    mb   = (uint32_t*)(gfx_info.RDRAM + g_gdp.ti_address); //pointer to the first macro block
957    dst  = (uint16_t*)(gfx_info.RDRAM + gDP.colorImage.address);
958    dst += ul_x + ul_y * ci_width;
959 
960    /* YUV macro block contains 16x16 texture.
961     * we need to put it in the proper place inside cimg */
962    for (h = 0; h < 16; h++)
963    {
964       for (w = 0; w < 16; w+=2)
965       {
966          uint32_t t = *(mb++); /* each uint32_t contains 2 pixels */
967          if ((h < height) && (w < width)) /* clipping. texture image may be larger than color image */
968          {
969             uint8_t y0 = (uint8_t)t&0xFF;
970             uint8_t v  = (uint8_t)(t>>8)&0xFF;
971             uint8_t y1 = (uint8_t)(t>>16)&0xFF;
972             uint8_t u  = (uint8_t)(t>>24)&0xFF;
973             *(dst++)   = YUVtoRGBA16(y0, u, v);
974             *(dst++)   = YUVtoRGBA16(y1, u, v);
975          }
976       }
977       dst += ci_width - 16;
978    }
979 }
980 
uc6_obj_rectangle_r(uint32_t w0,uint32_t w1)981 static void uc6_obj_rectangle_r(uint32_t w0, uint32_t w1)
982 {
983    int i;
984    float Z, ul_x, lr_x, ul_y, lr_y, ul_u, ul_v, lr_u, lr_v;
985    VERTEX v[4];
986    DRAWOBJECT d;
987 
988    uc6_read_object_data(&d);
989 
990    if (d.imageFmt == G_IM_FMT_YUV && (settings.hacks&hack_Ogre64)) //Ogre Battle needs to copy YUV texture to frame buffer
991    {
992       ul_x = d.objX / gSP.objMatrix.baseScaleX + gSP.objMatrix.X;
993       lr_x = (d.objX + d.imageW / d.scaleW) / gSP.objMatrix.baseScaleX + gSP.objMatrix.X;
994       ul_y = d.objY / gSP.objMatrix.baseScaleY + gSP.objMatrix.Y;
995       lr_y = (d.objY + d.imageH / d.scaleH) / gSP.objMatrix.baseScaleY + gSP.objMatrix.Y;
996       DrawYUVImageToFrameBuffer((uint16_t)ul_x, (uint16_t)ul_y, (uint16_t)lr_x, (uint16_t)lr_y);
997       return;
998    }
999 
1000    uc6_init_tile(&d);
1001 
1002    Z    = set_sprite_combine_mode();
1003 
1004    ul_x = d.objX / gSP.objMatrix.baseScaleX;
1005    lr_x = (d.objX + d.imageW / d.scaleW) / gSP.objMatrix.baseScaleX;
1006    ul_y = d.objY / gSP.objMatrix.baseScaleY;
1007    lr_y = (d.objY + d.imageH / d.scaleH) / gSP.objMatrix.baseScaleY;
1008    lr_u = 255.0f * rdp.cur_cache[0]->scale_x;
1009    lr_v = 255.0f * rdp.cur_cache[0]->scale_y;
1010 
1011    if (d.imageFlags & G_BG_FLAG_FLIPS) //flipS
1012    {
1013       ul_u = lr_u;
1014       lr_u = 0.5f;
1015    }
1016    else
1017       ul_u = 0.5f;
1018    if (d.imageFlags & G_BG_FLAG_FLIPT) //flipT
1019    {
1020       ul_v = lr_v;
1021       lr_v = 0.5f;
1022    }
1023    else
1024       ul_v = 0.5f;
1025 
1026    /* Make the vertices */
1027 
1028    v[0].x = ul_x;
1029    v[0].y = ul_y;
1030    v[0].z = Z;
1031    v[0].q = 1.0f;
1032    v[0].u[0] = ul_u;
1033    v[0].v[0] = ul_v;
1034 
1035    v[1].x = lr_x;
1036    v[1].y = ul_y;
1037    v[1].z = Z;
1038    v[1].q = 1.0f;
1039    v[1].u[0] = lr_u;
1040    v[1].v[0] = ul_v;
1041 
1042    v[2].x = ul_x;
1043    v[2].y = lr_y;
1044    v[2].z = Z;
1045    v[2].q = 1.0f;
1046    v[2].u[0] = ul_u;
1047    v[2].v[0] = lr_v;
1048 
1049    v[3].x = lr_x;
1050    v[3].y = lr_y;
1051    v[3].z = Z;
1052    v[3].q = 1.0f;
1053    v[3].u[0] = lr_u;
1054    v[3].v[0] = lr_v;
1055 
1056    for (i = 0; i < 4; i++)
1057    {
1058       v[i].x = ((v[i].x + gSP.objMatrix.X) * rdp.scale_x) + rdp.offset_x;
1059       v[i].y = ((v[i].y + gSP.objMatrix.Y) * rdp.scale_y) + rdp.offset_y;
1060    }
1061 
1062    uc6_draw_polygons (v);
1063 }
1064 
uc6_obj_loadtxtr(uint32_t w0,uint32_t w1)1065 static void uc6_obj_loadtxtr(uint32_t w0, uint32_t w1)
1066 {
1067    rdp.s2dex_tex_loaded = true;
1068    g_gdp.flags |= UPDATE_TEXTURE;
1069 
1070    glide64gSPObjLoadTxtr(w1);
1071 }
1072 
uc6_obj_ldtx_sprite(uint32_t w0,uint32_t w1)1073 static void uc6_obj_ldtx_sprite(uint32_t w0, uint32_t w1)
1074 {
1075    uint32_t addr = w1;
1076    uc6_obj_loadtxtr(__RSP.w0, __RSP.w1);
1077    __RSP.w1 = addr + 24;
1078    uc6_obj_sprite(__RSP.w0, __RSP.w1);
1079 }
1080 
uc6_obj_ldtx_rect(uint32_t w0,uint32_t w1)1081 static void uc6_obj_ldtx_rect(uint32_t w0, uint32_t w1)
1082 {
1083    uint32_t addr = w1;
1084    uc6_obj_loadtxtr(__RSP.w0, __RSP.w1);
1085    __RSP.w1 = addr + 24;
1086    uc6_obj_rectangle(__RSP.w0, __RSP.w1);
1087 }
1088 
uc6_ldtx_rect_r(uint32_t w0,uint32_t w1)1089 static void uc6_ldtx_rect_r(uint32_t w0, uint32_t w1)
1090 {
1091    uint32_t addr = w1;
1092    uc6_obj_loadtxtr(__RSP.w0, __RSP.w1);
1093    __RSP.w1 = addr + 24;
1094    uc6_obj_rectangle_r(__RSP.w0, __RSP.w1);
1095 }
1096 
uc6_loaducode(uint32_t w0,uint32_t w1)1097 static void uc6_loaducode(uint32_t w0, uint32_t w1)
1098 {
1099    /* copy the microcode data */
1100    uint32_t addr = RSP_SegmentToPhysical(w1);
1101    uint32_t size = (w0 & 0xFFFF) + 1;
1102    memcpy (microcode, gfx_info.RDRAM+addr, size);
1103 
1104    microcheck ();
1105 }
1106 
uc6_sprite2d(uint32_t w0,uint32_t w1)1107 static void uc6_sprite2d(uint32_t w0, uint32_t w1)
1108 {
1109    uint16_t stride;
1110    uint32_t addr, tlut;
1111    int i;
1112    DRAWIMAGE d;
1113    uint32_t    a = __RSP.PC[__RSP.PCi] & BMASK;
1114    uint32_t cmd0 = ((uint32_t*)gfx_info.RDRAM)[a>>2]; //check next command
1115 
1116    if ( (cmd0>>24) != 0xBE )
1117       return;
1118 
1119    addr = RSP_SegmentToPhysical(w1) >> 1;
1120 
1121    d.imagePtr = RSP_SegmentToPhysical(((uint32_t*)gfx_info.RDRAM)[(addr+0)>>1]); // 0,1
1122    stride     = (((uint16_t *)gfx_info.RDRAM)[(addr+4)^1]); // 4
1123    d.imageW   = (((uint16_t *)gfx_info.RDRAM)[(addr+5)^1]); // 5
1124    d.imageH   = (((uint16_t *)gfx_info.RDRAM)[(addr+6)^1]); // 6
1125    d.imageFmt = ((uint8_t *)gfx_info.RDRAM)[(((addr+7)<<1)+0)^3]; // 7
1126    d.imageSiz = ((uint8_t *)gfx_info.RDRAM)[(((addr+7)<<1)+1)^3]; // |
1127    d.imagePal = 0;
1128    d.imageX   = (((uint16_t *)gfx_info.RDRAM)[(addr+8)^1]); // 8
1129    d.imageY   = (((uint16_t *)gfx_info.RDRAM)[(addr+9)^1]); // 9
1130    tlut       = ((uint32_t*)gfx_info.RDRAM)[(addr + 2) >> 1]; // 2, 3
1131 
1132    /*low-level implementation of sprite2d apparently calls setothermode command to set tlut mode
1133     *However, description of sprite2d microcode just says that
1134     *TlutPointer should be Null when CI images will not be used.
1135     *HLE implementation sets rdp.tlut_mode=2 if TlutPointer is not null, and rdp.tlut_mode=0 otherwise
1136     *Alas, it is not sufficient, since WCW Nitro uses non-Null TlutPointer for rgba textures.
1137     *So, additional check added.
1138     */
1139    rdp.tlut_mode = 0;
1140    if (tlut)
1141    {
1142       load_palette (RSP_SegmentToPhysical(tlut), 0, 256);
1143       if (d.imageFmt > G_IM_FMT_RGBA)
1144          rdp.tlut_mode = 2;
1145       else
1146          rdp.tlut_mode = 0;
1147    }
1148 
1149    if (d.imageW == 0)
1150       return;// d.imageW = stride;
1151 
1152    cmd0 = ((uint32_t*)gfx_info.RDRAM)[a>>2]; //check next command
1153    while (1)
1154    {
1155       uint32_t texsize, maxTexSize;
1156       if ( (cmd0>>24) == 0xBE )
1157       {
1158          uint32_t cmd1    = ((uint32_t*)gfx_info.RDRAM)[(a>>2)+1];
1159          __RSP.PC[__RSP.PCi] = (a+8) & BMASK;
1160 
1161          d.scaleX = ((cmd1 >> 16)&0xFFFF)/1024.0f;
1162          d.scaleY = (cmd1  &  0xFFFF)/1024.0f;
1163 
1164          //the code below causes wrong background height in super robot spirit, so it is disabled.
1165          //need to find, for which game this hack was made
1166          //if( (cmd1&0xFFFF) < 0x100 )
1167          // d.scaleY = d.scaleX;
1168          d.flipX          = (uint8_t)((cmd0>>8)&0xFF);
1169          d.flipY          = (uint8_t)(cmd0&0xFF);
1170 
1171          a                   = __RSP.PC[__RSP.PCi] & BMASK;
1172          __RSP.PC[__RSP.PCi] = (a+8) & BMASK;
1173          cmd0                = ((uint32_t*)gfx_info.RDRAM)[a>>2]; //check next command
1174       }
1175       if ( (cmd0>>24) == 0xBD )
1176       {
1177          uint32_t cmd1 = ((uint32_t*)gfx_info.RDRAM)[(a>>2)+1];
1178 
1179          d.frameX = ((int16_t)((cmd1>>16)&0xFFFF)) / 4.0f;
1180          d.frameY = ((int16_t)(cmd1&0xFFFF)) / 4.0f;
1181          d.frameW = (uint16_t) (d.imageW / d.scaleX);
1182          d.frameH = (uint16_t) (d.imageH / d.scaleY);
1183          if (settings.hacks&hack_WCWnitro)
1184          {
1185             int scaleY = (int)d.scaleY;
1186             d.imageH /= scaleY;
1187             d.imageY /= scaleY;
1188             stride *= scaleY;
1189             d.scaleY = 1.0f;
1190          }
1191 #if 0
1192          FRDP ("imagePtr: %08lx\n", d.imagePtr);
1193          FRDP ("frameX: %f, frameW: %d, frameY: %f, frameH: %d\n", d.frameX, d.frameW, d.frameY, d.frameH);
1194          FRDP ("imageX: %d, imageW: %d, imageY: %d, imageH: %d\n", d.imageX, d.imageW, d.imageY, d.imageH);
1195          FRDP ("imageFmt: %d, imageSiz: %d, imagePal: %d, imageStride: %d\n", d.imageFmt, d.imageSiz, d.imagePal, stride);
1196          FRDP ("scaleX: %f, scaleY: %f\n", d.scaleX, d.scaleY);
1197 #endif
1198       }
1199       else
1200          return;
1201 
1202       texsize = (d.imageW * d.imageH) << d.imageSiz >> 1;
1203       maxTexSize = rdp.tlut_mode < G_TT_RGBA16 ? 4096 : 2048;
1204 
1205       if (texsize > maxTexSize)
1206       {
1207          if (d.scaleX != 1)
1208             d.scaleX *= (float)stride/(float)d.imageW;
1209          d.imageW = stride;
1210          d.imageH += d.imageY;
1211          DrawImage(&d);
1212       }
1213       else
1214       {
1215          float Z, ul_x, ul_y, lr_x, lr_y, lr_u, lr_v;
1216          struct gDPTile *tile;
1217          VERTEX v[4];
1218          uint16_t line = d.imageW;
1219 
1220          if (line & 7) line += 8; // round up
1221          line >>= 3;
1222          if (d.imageSiz == 0)
1223          {
1224             if (line%2)
1225                line++;
1226             line >>= 1;
1227          }
1228          else
1229          {
1230             line <<= (d.imageSiz-1);
1231          }
1232          if (line == 0)
1233             line = 1;
1234 
1235          glide64gDPSetTextureImage(
1236                g_gdp.ti_format,        /* format */
1237                g_gdp.ti_size,          /* siz */
1238                stride,                 /* width */
1239                d.imagePtr              /* address */
1240                );
1241 
1242          g_gdp.tile[7].tmem = 0;
1243          g_gdp.tile[7].line = line;//(d.imageW>>3);
1244          g_gdp.tile[7].size = d.imageSiz;
1245          __RSP.w0           = (d.imageX << 14) | (d.imageY << 2);
1246          __RSP.w1           = 0x07000000 | ((d.imageX+d.imageW-1) << 14) | ((d.imageY+d.imageH-1) << 2);
1247          rdp_loadtile(__RSP.w0, __RSP.w1);
1248 
1249          glide64gDPSetTile(
1250                d.imageFmt,
1251                d.imageSiz,
1252                line, /* (d.imageW>>3)  */
1253                0,
1254                0,
1255                0,
1256                0,
1257                0,
1258                0,
1259                0,
1260                0,
1261                0);
1262 
1263          glide64gDPSetTileSize(
1264                0,                      /* tile */
1265                d.imageX,               /* uls  */
1266                d.imageY,               /* ult  */
1267                d.imageX+d.imageW-1,    /* lrs  */
1268                d.imageY+d.imageH-1     /* lrt  */
1269                );
1270 
1271          Z = set_sprite_combine_mode ();
1272 
1273          if (d.flipX)
1274          {
1275             ul_x = d.frameX + d.frameW;
1276             lr_x = d.frameX;
1277          }
1278          else
1279          {
1280             ul_x = d.frameX;
1281             lr_x = d.frameX + d.frameW;
1282          }
1283          if (d.flipY)
1284          {
1285             ul_y = d.frameY + d.frameH;
1286             lr_y = d.frameY;
1287          }
1288          else
1289          {
1290             ul_y = d.frameY;
1291             lr_y = d.frameY + d.frameH;
1292          }
1293 
1294 #if 0
1295          if (rdp.cur_cache[0]->splits > 1)
1296          {
1297             lr_u = (float)(d.imageW-1);
1298             lr_v = (float)(d.imageH-1);
1299          }
1300          else
1301 #endif
1302          {
1303             lr_u = 255.0f*rdp.cur_cache[0]->scale_x;
1304             lr_v = 255.0f*rdp.cur_cache[0]->scale_y;
1305          }
1306 
1307          /* Make the vertices */
1308 
1309          v[0].x = ul_x;
1310          v[0].y = ul_y;
1311          v[0].z = Z;
1312          v[0].q = 1.0f;
1313          v[0].u[0] = 0.5f;
1314          v[0].v[0] = 0.5f;
1315 
1316          v[1].x = lr_x;
1317          v[1].y = ul_y;
1318          v[1].z = Z;
1319          v[1].q = 1.0f;
1320          v[1].u[0] = lr_u;
1321          v[1].v[0] = 0.5f;
1322 
1323          v[2].x = ul_x;
1324          v[2].y = lr_y;
1325          v[2].z = Z;
1326          v[2].q = 1.0f;
1327          v[2].u[0] = 0.5f;
1328          v[2].v[0] = lr_v;
1329 
1330          v[3].x = lr_x;
1331          v[3].y = lr_y;
1332          v[3].z = Z;
1333          v[3].q = 1.0f;
1334          v[3].u[0] = lr_u;
1335          v[3].v[0] = lr_v;
1336 
1337          for (i=0; i<4; i++)
1338          {
1339             v[i].x = (v[i].x * rdp.scale_x) + rdp.offset_x;
1340             v[i].y = (v[i].y * rdp.scale_y) + rdp.offset_y;
1341          }
1342 
1343          apply_shading(v);
1344 
1345 #if 0
1346          // Set vertex buffers
1347          if (rdp.cur_cache[0]->splits > 1)
1348          {
1349             VERTEX *vptr[3];
1350             for (i = 0; i < 3; i++)
1351                vptr[i] = &v[i];
1352             draw_split_triangle(vptr);
1353 
1354             for (i = 0; i < 3; i++)
1355                vptr[i] = &v[i+1];
1356             draw_split_triangle(vptr);
1357          }
1358          else
1359 #endif
1360          {
1361             rdp.vtxbuf = rdp.vtx1; // copy from v to rdp.vtx1
1362             rdp.vtxbuf2 = rdp.vtx2;
1363             rdp.vtx_buffer = 0;
1364             rdp.n_global = 3;
1365             memcpy (rdp.vtxbuf, v, sizeof(VERTEX)*3);
1366             do_triangle_stuff_2 (0, 1, 1);
1367 
1368             rdp.vtxbuf = rdp.vtx1; // copy from v to rdp.vtx1
1369             rdp.vtxbuf2 = rdp.vtx2;
1370             rdp.vtx_buffer = 0;
1371             rdp.n_global = 3;
1372             memcpy (rdp.vtxbuf, v+1, sizeof(VERTEX)*3);
1373             do_triangle_stuff_2 (0, 1, 1);
1374          }
1375          g_gdp.flags |= UPDATE_ZBUF_ENABLED | UPDATE_VIEWPORT;
1376 
1377          if (settings.fog && (rdp.flags & FOG_ENABLED))
1378             grFogMode (GR_FOG_WITH_TABLE_ON_FOGCOORD_EXT, g_gdp.fog_color.total);
1379 
1380       }
1381       a    = __RSP.PC[__RSP.PCi] & BMASK;
1382       cmd0 = ((uint32_t*)gfx_info.RDRAM)[a>>2]; //check next command
1383       if (( (cmd0>>24) == 0xBD ) || ( (cmd0>>24) == 0xBE ))
1384          __RSP.PC[__RSP.PCi] = (a+8) & BMASK;
1385       else
1386          return;
1387    }
1388 }
1389