8359ee47877d39285cd7af91e9a3f49c0a5f226b
[mupen64plus-pandora.git] / source / rice_gles / src / Render.cpp
1 /*
2 Copyright (C) 2003 Rice1964
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17
18 */
19
20 #define M64P_PLUGIN_PROTOTYPES 1
21 #include "osal_preproc.h"
22 #include "m64p_plugin.h"
23
24 #include "ConvertImage.h"
25 #include "DeviceBuilder.h"
26 #include "FrameBuffer.h"
27 #include "Render.h"
28
29 #include "liblinux/BMGLibPNG.h"
30
31 #include <algorithm>
32
33 extern FiddledVtx * g_pVtxBase;
34 CRender * CRender::g_pRender=NULL;
35 int CRender::gRenderReferenceCount=0;
36
37 XMATRIX reverseXY(-1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1);
38 XMATRIX reverseY(1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1);
39 extern char* right (const char * src, int nchars);
40
41 #if defined(WIN32)
42   #define strcasecmp _stricmp
43 #endif
44
45 #ifdef min
46 #undef min
47 #endif
48 #ifdef max
49 #undef max
50 #endif
51
52 //========================================================================
53 CRender * CRender::GetRender(void)
54 {
55     if( CRender::g_pRender == NULL )
56     {
57         DebugMessage(M64MSG_ERROR, "g_pRender is NULL");
58         exit(0);
59     }
60     else
61         return CRender::g_pRender;
62 }
63 bool CRender::IsAvailable()
64 {
65     return CRender::g_pRender != NULL;
66 }
67
68 CRender::CRender() :
69     m_fScreenViewportMultX(2.0f),
70     m_fScreenViewportMultY(2.0f),
71
72     m_dwTexturePerspective(FALSE),
73     m_bAlphaTestEnable(FALSE),
74         m_bZUpdate(FALSE),
75         m_bZCompare(FALSE),
76         m_dwZBias(0),
77     
78     m_dwMinFilter(FILTER_POINT),
79     m_dwMagFilter(FILTER_POINT),
80         m_dwAlpha(0xFF),
81         m_Mux(0),
82         m_bBlendModeValid(FALSE)
83 {
84     int i;
85     InitRenderBase();
86
87     for( i=0; i<MAX_TEXTURES; i++ )
88     {
89         g_textures[i].m_lpsTexturePtr = NULL;
90         g_textures[i].m_pCTexture = NULL;
91         
92         g_textures[i].m_dwTileWidth = 64;       // Value doesn't really matter, as tex not set
93         g_textures[i].m_dwTileHeight = 64;
94         g_textures[i].m_fTexWidth = 64.0f;      // Value doesn't really matter, as tex not set
95         g_textures[i].m_fTexHeight = 64.0f;
96         g_textures[i].pTextureEntry = NULL;
97
98         TileUFlags[i] = TileVFlags[i] = TEXTURE_UV_FLAG_CLAMP;
99     }
100
101
102     //for( i=0; i<MAX_VERTS; i++)
103     //{
104     //  g_dwVtxFlags[i] = 0;
105     //}
106     
107     m_pColorCombiner = CDeviceBuilder::GetBuilder()->CreateColorCombiner(this);
108     m_pColorCombiner->Initialize();
109
110     m_pAlphaBlender = CDeviceBuilder::GetBuilder()->CreateAlphaBlender(this);
111
112 }
113
114 CRender::~CRender()
115 {
116     if( m_pColorCombiner != NULL )
117     {
118         CDeviceBuilder::GetBuilder()->DeleteColorCombiner();
119         m_pColorCombiner = NULL;
120     }
121     
122     if( m_pAlphaBlender != NULL )
123     {
124         CDeviceBuilder::GetBuilder()->DeleteAlphaBlender();
125         m_pAlphaBlender = NULL;
126     }
127 }
128
129 void CRender::ResetMatrices()
130 {
131     Matrix mat;
132
133     mat.m[0][1] = mat.m[0][2] = mat.m[0][3] =
134     mat.m[1][0] = mat.m[1][2] = mat.m[1][3] =
135     mat.m[2][0] = mat.m[2][1] = mat.m[2][3] =
136     mat.m[3][0] = mat.m[3][1] = mat.m[3][2] = 0.0f;
137
138     mat.m[0][0] = mat.m[1][1] = mat.m[2][2] = mat.m[3][3] = 1.0f;
139
140     gRSP.projectionMtxTop = 0;
141     gRSP.modelViewMtxTop = 0;
142     gRSP.projectionMtxs[0] = mat;
143     gRSP.modelviewMtxs[0] = mat;
144
145     gRSP.bMatrixIsUpdated = true;
146     gRSP.bWorldMatrixIsUpdated = true;
147     UpdateCombinedMatrix();
148 }
149
150 void CRender::SetProjection(const Matrix & mat, bool bPush, bool bReplace) 
151 {
152     if (bPush)
153     {
154         if (gRSP.projectionMtxTop >= (RICE_MATRIX_STACK-1))
155         {
156             TRACE0("Pushing past proj stack limits!");
157         }
158         else
159             gRSP.projectionMtxTop++;
160
161         if (bReplace)
162         {
163             // Load projection matrix
164             gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat;
165         }
166         else
167         {
168             gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat * gRSP.projectionMtxs[gRSP.projectionMtxTop-1];
169         }
170         
171     }
172     else
173     {
174         if (bReplace)
175         {
176             // Load projection matrix
177             gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat;
178         }
179         else
180         {
181             gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat * gRSP.projectionMtxs[gRSP.projectionMtxTop];
182         }
183     }
184     
185     gRSP.bMatrixIsUpdated = true;
186
187     DumpMatrix(mat,"Set Projection Matrix");
188 }
189
190 bool mtxPopUpError = false;
191 void CRender::SetWorldView(const Matrix & mat, bool bPush, bool bReplace)
192 {
193     if (bPush)
194     {
195         if (gRSP.modelViewMtxTop >= (RICE_MATRIX_STACK-1))
196             DebuggerAppendMsg("Pushing past modelview stack limits! %s", bReplace?"Load":"Mul");
197         else
198             gRSP.modelViewMtxTop++;
199
200         // We should store the current projection matrix...
201         if (bReplace)
202         {
203             // Load projection matrix
204             gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat;
205
206             // Hack needed to show heart in OOT & MM
207             // It renders at Z cordinate = 0.0f that gets clipped away
208             // So we translate them a bit along Z to make them stick
209             if( options.enableHackForGames == HACK_FOR_ZELDA || options.enableHackForGames == HACK_FOR_ZELDA_MM) 
210             {
211                 if(gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._43 == 0.0f
212                     && gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._42 != 0.0f
213                     && gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._42 <= 94.5f
214                     && gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._42 >= -94.5f)
215                 {
216                     gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._43 -= 10.1f;
217                 }
218             }
219         }
220         else            // Multiply projection matrix
221         {
222             gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat * gRSP.modelviewMtxs[gRSP.modelViewMtxTop-1];
223         }
224     }
225     else    // NoPush
226     {
227         if (bReplace)
228         {
229             // Load projection matrix
230             gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat;
231         }
232         else
233         {
234             // Multiply projection matrix
235             gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat * gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
236         }
237     }
238
239     gRSPmodelViewTop = gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
240     if( options.enableHackForGames == HACK_REVERSE_XY_COOR )
241     {
242         gRSPmodelViewTop = gRSPmodelViewTop * reverseXY;
243     }
244     if( options.enableHackForGames == HACK_REVERSE_Y_COOR )
245     {
246         gRSPmodelViewTop = gRSPmodelViewTop * reverseY;
247     }
248     MatrixTranspose(&gRSPmodelViewTopTranspose, &gRSPmodelViewTop);
249
250     gRSP.bMatrixIsUpdated = true;
251     gRSP.bWorldMatrixIsUpdated = true;
252
253     DumpMatrix(mat,"Set WorldView Matrix");
254 }
255
256
257 void CRender::PopWorldView()
258 {
259     if (gRSP.modelViewMtxTop > 0)
260     {
261         gRSP.modelViewMtxTop--;
262         gRSPmodelViewTop = gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
263         if( options.enableHackForGames == HACK_REVERSE_XY_COOR )
264         {
265             gRSPmodelViewTop = gRSPmodelViewTop * reverseXY;
266         }
267         if( options.enableHackForGames == HACK_REVERSE_Y_COOR )
268         {
269             gRSPmodelViewTop = gRSPmodelViewTop * reverseY;
270         }
271         MatrixTranspose(&gRSPmodelViewTopTranspose, &gRSPmodelViewTop);
272         gRSP.bMatrixIsUpdated = true;
273         gRSP.bWorldMatrixIsUpdated = true;
274     }
275     else
276     {
277 #ifdef DEBUGGER
278         if( pauseAtNext )
279             TRACE0("Popping past worldview stack limits");
280 #endif
281         mtxPopUpError = true;
282     }
283 }
284
285
286 Matrix & CRender::GetWorldProjectMatrix(void)
287 {
288     return gRSPworldProject;
289 }
290
291 void CRender::SetWorldProjectMatrix(Matrix &mtx)
292 {
293 #ifdef DEBUGGER
294     if( pauseAtNext && (eventToPause == NEXT_TRIANGLE || eventToPause == NEXT_FLUSH_TRI || eventToPause == NEXT_MATRIX_CMD ) )
295     {
296         uint32 dwPC = gDlistStack[gDlistStackPointer].pc-8;
297         DebuggerAppendMsg("Force Matrix: pc=%08X", dwPC);
298         DumpMatrix(mtx, "Force Matrix, loading new world-project matrix");
299     }
300 #endif
301     gRSPworldProject = mtx;
302
303     gRSP.bMatrixIsUpdated = false;
304     gRSP.bCombinedMatrixIsUpdated = true;
305 }
306
307 void CRender::SetMux(uint32 dwMux0, uint32 dwMux1)
308 {
309     uint64 tempmux = (((uint64)dwMux0) << 32) | (uint64)dwMux1;
310     if( m_Mux != tempmux )
311     {
312         m_Mux = tempmux;
313         m_bBlendModeValid = FALSE;
314         m_pColorCombiner->UpdateCombiner(dwMux0, dwMux1);
315     }
316 }
317
318
319 void CRender::SetCombinerAndBlender()
320 {
321     InitOtherModes();
322
323     if( g_curRomInfo.bDisableBlender )
324         m_pAlphaBlender->DisableAlphaBlender();
325     else if( currentRomOptions.bNormalBlender )
326         m_pAlphaBlender->NormalAlphaBlender();
327     else
328         m_pAlphaBlender->InitBlenderMode();
329
330     m_pColorCombiner->InitCombinerMode();
331 }
332
333 void CRender::RenderReset()
334 {
335     UpdateClipRectangle();
336     ResetMatrices();
337     SetZBias(0);
338     gRSP.numVertices = 0;
339     gRSP.maxVertexID = 0;
340     gRSP.curTile = 0;
341     gRSP.fTexScaleX = 1/32.0f;;
342     gRSP.fTexScaleY = 1/32.0f;
343 }
344
345 bool CRender::FillRect(int nX0, int nY0, int nX1, int nY1, uint32 dwColor)
346 {
347     LOG_UCODE("FillRect: X0=%d, Y0=%d, X1=%d, Y1=%d, Color=0x%8X", nX0, nY0, nX1, nY1, dwColor);
348
349     if (g_CI.dwSize != TXT_SIZE_16b && frameBufferOptions.bIgnore) 
350         return true;
351
352     if (status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM)
353         status.bFrameBufferIsDrawn = true;
354
355     if(status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE)
356     {
357         status.bVIOriginIsUpdated=false;
358         CGraphicsContext::Get()->UpdateFrame();
359         DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG, {DebuggerAppendMsg("Screen Update at 1st FillRectangle");});
360     }
361
362   if (status.bCIBufferIsRendered && status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_BEFORE_SCREEN_CLEAR )
363   {
364       if ((nX0==0 && nY0 == 0 && (nX1 == (int) g_CI.dwWidth || nX1 == (int) g_CI.dwWidth-1)) ||
365           (nX0==gRDP.scissor.left && nY0 == gRDP.scissor.top  && (nX1 == gRDP.scissor.right || nX1 == gRDP.scissor.right-1)) ||
366           ((nX0+nX1 == (int)g_CI.dwWidth || nX0+nX1 == (int)g_CI.dwWidth-1 || nX0+nX1 == gRDP.scissor.left+gRDP.scissor.right || nX0+nX1 == gRDP.scissor.left+gRDP.scissor.right-1) && (nY0 == gRDP.scissor.top || nY0 == 0 || nY0+nY1 == gRDP.scissor.top+gRDP.scissor.bottom || nY0+nY1 == gRDP.scissor.top+gRDP.scissor.bottom-1)))
367       {
368           status.bVIOriginIsUpdated=false;
369           CGraphicsContext::Get()->UpdateFrame();
370           DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG,{DebuggerAppendMsg("Screen Update Before Screen Clear");});
371       }
372   }
373
374
375     SetFillMode(RICE_FILLMODE_SOLID);
376
377     bool res=true;
378
379     /*
380     // I don't know why this does not work for OpenGL
381     if( gRDP.otherMode.cycle_type == CYCLE_TYPE_FILL && nX0 == 0 && nY0 == 0 && ((nX1==windowSetting.uViWidth && nY1==windowSetting.uViHeight)||(nX1==windowSetting.uViWidth-1 && nY1==windowSetting.uViHeight-1)) )
382     {
383         CGraphicsContext::g_pGraphicsContext->Clear(CLEAR_COLOR_BUFFER,dwColor);
384     }
385     else
386     */
387     {
388         //BOOL m_savedZBufferFlag = gRSP.bZBufferEnabled;   // Save ZBuffer state
389         ZBufferEnable( FALSE );
390
391         m_fillRectVtx[0].x = ViewPortTranslatei_x(nX0);
392         m_fillRectVtx[0].y = ViewPortTranslatei_y(nY0);
393         m_fillRectVtx[1].x = ViewPortTranslatei_x(nX1);
394         m_fillRectVtx[1].y = ViewPortTranslatei_y(nY1);
395
396         SetCombinerAndBlender();
397
398         if( gRDP.otherMode.cycle_type  >= CYCLE_TYPE_COPY )
399         {
400             ZBufferEnable(FALSE);
401         }
402         else
403         {
404             //dwColor = PostProcessDiffuseColor(0);
405             dwColor = PostProcessDiffuseColor(gRDP.primitiveColor);
406         }
407
408         float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
409
410         ApplyRDPScissor();
411         TurnFogOnOff(false);
412         res = RenderFillRect(dwColor, depth);
413         TurnFogOnOff(gRSP.bFogEnabled);
414
415         if( gRDP.otherMode.cycle_type  >= CYCLE_TYPE_COPY )
416         {
417             ZBufferEnable(gRSP.bZBufferEnabled);
418         }
419     }
420
421     if( options.bWinFrameMode ) SetFillMode(RICE_FILLMODE_WINFRAME );
422
423     DEBUGGER_PAUSE_AND_DUMP_COUNT_N( NEXT_FILLRECT, {DebuggerAppendMsg("FillRect: X0=%d, Y0=%d, X1=%d, Y1=%d, Color=0x%08X", nX0, nY0, nX1, nY1, dwColor);
424             DebuggerAppendMsg("Pause after FillRect: Color=%08X\n", dwColor);if( logCombiners ) m_pColorCombiner->DisplayMuxString();});
425     DEBUGGER_PAUSE_AND_DUMP_COUNT_N( NEXT_FLUSH_TRI, {DebuggerAppendMsg("FillRect: X0=%d, Y0=%d, X1=%d, Y1=%d, Color=0x%08X", nX0, nY0, nX1, nY1, dwColor);
426             DebuggerAppendMsg("Pause after FillRect: Color=%08X\n", dwColor);if( logCombiners ) m_pColorCombiner->DisplayMuxString();});
427
428     return res;
429 }
430
431
432 bool CRender::Line3D(uint32 dwV0, uint32 dwV1, uint32 dwWidth)
433 {
434     LOG_UCODE("Line3D: Vtx0=%d, Vtx1=%d, Width=%d", dwV0, dwV1, dwWidth);
435     if( !status.bCIBufferIsRendered ) g_pFrameBufferManager->ActiveTextureBuffer();
436
437     m_line3DVtx[0].z = (g_vecProjected[dwV0].z + 1.0f) * 0.5f;
438     m_line3DVtx[1].z = (g_vecProjected[dwV1].z + 1.0f) * 0.5f;
439
440     if( m_line3DVtx[0].z != m_line3DVtx[1].z )  
441         return false;
442
443     if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )   status.bFrameBufferIsDrawn = true;
444     if( status.bHandleN64RenderTexture ) 
445     {
446         g_pRenderTextureInfo->maxUsedHeight = g_pRenderTextureInfo->N64Height;
447         if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )   
448         {
449             status.bFrameBufferIsDrawn = true;
450             status.bFrameBufferDrawnByTriangles = true;
451         }
452     }
453
454     m_line3DVtx[0].x = ViewPortTranslatef_x(g_vecProjected[dwV0].x);
455     m_line3DVtx[0].y = ViewPortTranslatef_y(g_vecProjected[dwV0].y);
456     m_line3DVtx[0].rhw = g_vecProjected[dwV0].w;
457     m_line3DVtx[0].dcDiffuse = PostProcessDiffuseColor(g_dwVtxDifColor[dwV0]);
458     m_line3DVtx[0].dcSpecular = PostProcessSpecularColor();
459
460     m_line3DVtx[1].x = ViewPortTranslatef_x(g_vecProjected[dwV1].x);
461     m_line3DVtx[1].y = ViewPortTranslatef_y(g_vecProjected[dwV1].y);
462     m_line3DVtx[1].rhw = g_vecProjected[dwV1].w;
463     m_line3DVtx[1].dcDiffuse = PostProcessDiffuseColor(g_dwVtxDifColor[dwV1]);
464     m_line3DVtx[1].dcSpecular = m_line3DVtx[0].dcSpecular;
465
466     float width = dwWidth*0.5f+1.5f;
467
468     if( m_line3DVtx[0].y == m_line3DVtx[1].y )
469     {
470         m_line3DVector[0].x = m_line3DVector[1].x = m_line3DVtx[0].x;
471         m_line3DVector[2].x = m_line3DVector[3].x = m_line3DVtx[1].x;
472
473         m_line3DVector[0].y = m_line3DVector[2].y = m_line3DVtx[0].y-width/2*windowSetting.fMultY;
474         m_line3DVector[1].y = m_line3DVector[3].y = m_line3DVtx[0].y+width/2*windowSetting.fMultY;
475     }
476     else
477     {
478         m_line3DVector[0].y = m_line3DVector[1].y = m_line3DVtx[0].y;
479         m_line3DVector[2].y = m_line3DVector[3].y = m_line3DVtx[1].y;
480
481         m_line3DVector[0].x = m_line3DVector[2].x = m_line3DVtx[0].x-width/2*windowSetting.fMultX;
482         m_line3DVector[1].x = m_line3DVector[3].x = m_line3DVtx[0].x+width/2*windowSetting.fMultX;
483     }
484
485     SetCombinerAndBlender();
486
487     bool res=RenderLine3D();
488
489     DEBUGGER_PAUSE_AND_DUMP_COUNT_N(NEXT_FLUSH_TRI, {
490         DebuggerAppendMsg("Pause after Line3D: v%d(%f,%f,%f), v%d(%f,%f,%f), Width=%d\n", dwV0, m_line3DVtx[0].x, m_line3DVtx[0].y, m_line3DVtx[0].z, 
491             dwV1, m_line3DVtx[1].x, m_line3DVtx[1].y, m_line3DVtx[1].z, dwWidth);
492     });
493
494     DEBUGGER_PAUSE_AND_DUMP_COUNT_N(NEXT_OBJ_TXT_CMD, {
495         DebuggerAppendMsg("Pause after Line3D: v%d(%f,%f,%f), v%d(%f,%f,%f), Width=%d\n", dwV0, m_line3DVtx[0].x, m_line3DVtx[0].y, m_line3DVtx[0].z, 
496             dwV1, m_line3DVtx[1].x, m_line3DVtx[1].y, m_line3DVtx[1].z, dwWidth);
497     });
498
499     return res;
500 }
501
502 bool CRender::RemapTextureCoordinate
503     (float t0, float t1, uint32 tileWidth, uint32 mask, float textureWidth, float &u0, float &u1)
504 {
505     int s0 = (int)t0;
506     int s1 = (int)t1;
507     int width = mask>0 ? (1<<mask) : tileWidth;
508     if( width == 0 ) return false;
509
510     int divs0 = s0/width; if( divs0*width > s0 )    divs0--;
511     int divs1 = s1/width; if( divs1*width > s1 )    divs1--;
512
513     if( divs0 == divs1 )
514     {
515         s0 -= divs0*width;
516         s1 -= divs1*width;
517         //if( s0 > s1 ) 
518         //  s0++;
519         //else if( s1 > s0 )    
520         //  s1++;
521         u0 = s0/textureWidth;
522         u1 = s1/textureWidth;
523
524         return true;
525     }
526     else if( divs0+1 == divs1 && s0%width==0 && s1%width == 0 )
527     {
528         u0 = 0;
529         u1 = tileWidth/textureWidth;
530         return true;
531     }
532     else if( divs0 == divs1+1 && s0%width==0 && s1%width == 0 )
533     {
534         u1 = 0;
535         u0 = tileWidth/textureWidth;
536         return true;
537     }
538     else
539     {
540         //if( s0 > s1 ) 
541         //{
542             //s0++;
543         //  u0 = s0/textureWidth;
544         //}
545         //else if( s1 > s0 )    
546         //{
547             //s1++;
548         //  u1 = s1/textureWidth;
549         //}
550
551         return false;
552     }
553 }
554
555 bool CRender::TexRect(int nX0, int nY0, int nX1, int nY1, float fS0, float fT0, float fScaleS, float fScaleT, bool colorFlag, uint32 diffuseColor)
556 {
557     if( options.enableHackForGames == HACK_FOR_DUKE_NUKEM )
558     {
559         colorFlag = true;
560         diffuseColor = 0;
561     }
562
563     if( status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE )
564     {
565         status.bVIOriginIsUpdated=false;
566         CGraphicsContext::Get()->UpdateFrame();
567         DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG,{DebuggerAppendMsg("Screen Update at 1st textRect");});
568     }
569
570     if( options.enableHackForGames == HACK_FOR_BANJO_TOOIE )
571     {
572         // Hack for Banjo shadow in Banjo Tooie
573         if( g_TI.dwWidth == g_CI.dwWidth && g_TI.dwFormat == TXT_FMT_CI && g_TI.dwSize == TXT_SIZE_8b )
574         {
575             if( nX0 == fS0 && nY0 == fT0 )//&& nX0 > 90 && nY0 > 130 && nX1-nX0 > 80 && nY1-nY0 > 20 )
576             {
577                 // Skip the text rect
578                 return true;
579             }
580         }
581     }
582
583     if( status.bN64IsDrawingTextureBuffer )
584     {
585         if( frameBufferOptions.bIgnore || ( frameBufferOptions.bIgnoreRenderTextureIfHeightUnknown && newRenderTextureInfo.knownHeight == 0 ) )
586         {
587             return true;
588         }
589     }
590
591     PrepareTextures();
592
593     if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == TXT_SIZE_8b ) 
594     {
595         return true;
596     }
597
598     if( !IsTextureEnabled() &&  gRDP.otherMode.cycle_type  != CYCLE_TYPE_COPY )
599     {
600         FillRect(nX0, nY0, nX1, nY1, gRDP.primitiveColor);
601         return true;
602     }
603
604
605     if( IsUsedAsDI(g_CI.dwAddr) && !status.bHandleN64RenderTexture )
606     {
607         status.bFrameBufferIsDrawn = true;
608     }
609
610     if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )   status.bFrameBufferIsDrawn = true;
611
612     LOG_UCODE("TexRect: X0=%d, Y0=%d, X1=%d, Y1=%d,\n\t\tfS0=%f, fT0=%f, ScaleS=%f, ScaleT=%f ",
613         nX0, nY0, nX1, nY1, fS0, fT0, fScaleS, fScaleT);
614
615     if( options.bEnableHacks )
616     {
617         // Goldeneye HACK
618         if( nY1 - nY0 < 2 ) 
619             nY1 = nY1+2;
620
621         //// Text edge hack
622         else if( gRDP.otherMode.cycle_type == CYCLE_TYPE_1 && fScaleS == 1 && fScaleT == 1 && 
623             (int)g_textures[gRSP.curTile].m_dwTileWidth == nX1-nX0+1 && (int)g_textures[gRSP.curTile].m_dwTileHeight == nY1-nY0+1 &&
624             g_textures[gRSP.curTile].m_dwTileWidth%2 == 0 && g_textures[gRSP.curTile].m_dwTileHeight%2 == 0 )
625         {
626             nY1++;
627             nX1++;
628         }
629         else if( g_curRomInfo.bIncTexRectEdge )
630         {
631             nX1++;
632             nY1++;
633         }
634     }
635
636
637     // Scale to Actual texture coords
638     // The two cases are to handle the oversized textures hack on voodoos
639
640     SetCombinerAndBlender();
641     
642
643     if( gRDP.otherMode.cycle_type  >= CYCLE_TYPE_COPY || !gRDP.otherMode.z_cmp )
644     {
645         ZBufferEnable(FALSE);
646     }
647
648     BOOL accurate = currentRomOptions.bAccurateTextureMapping;
649
650     RenderTexture &tex0 = g_textures[gRSP.curTile];
651     Tile &tile0 = gRDP.tiles[gRSP.curTile];
652
653     float widthDiv = tex0.m_fTexWidth;
654     float heightDiv = tex0.m_fTexHeight;
655     float t0u0;
656     if( options.enableHackForGames == HACK_FOR_ALL_STAR_BASEBALL || options.enableHackForGames == HACK_FOR_MLB )
657     {
658         t0u0 = (fS0 -tile0.fhilite_sl);
659     }
660     else
661     {
662         t0u0 = (fS0 * tile0.fShiftScaleS -tile0.fhilite_sl);
663     }
664     
665     float t0u1;
666     if( accurate && gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY )
667     {
668         t0u1 = t0u0 + (fScaleS * (nX1 - nX0 - 1))*tile0.fShiftScaleS;
669     }
670     else
671     {
672         t0u1 = t0u0 + (fScaleS * (nX1 - nX0))*tile0.fShiftScaleS;
673     }
674
675
676     if( status.UseLargerTile[0] )
677     {
678         m_texRectTex1UV[0].u = (t0u0+status.LargerTileRealLeft[0])/widthDiv;
679         m_texRectTex1UV[1].u = (t0u1+status.LargerTileRealLeft[0])/widthDiv;
680     }
681     else
682     {
683         m_texRectTex1UV[0].u = t0u0/widthDiv;
684         m_texRectTex1UV[1].u = t0u1/widthDiv;
685         if( accurate && !tile0.bMirrorS && RemapTextureCoordinate(t0u0, t0u1, tex0.m_dwTileWidth, tile0.dwMaskS, widthDiv, m_texRectTex1UV[0].u, m_texRectTex1UV[1].u) )
686             SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
687     }
688
689     float t0v0;
690     if( options.enableHackForGames == HACK_FOR_ALL_STAR_BASEBALL || options.enableHackForGames == HACK_FOR_MLB )
691     {
692         t0v0 = (fT0 -tile0.fhilite_tl);
693     }
694     else
695     {
696         t0v0 = (fT0 * tile0.fShiftScaleT -tile0.fhilite_tl);
697     }
698     
699     float t0v1;
700     if ( accurate && gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY)
701     {
702         t0v1 = t0v0 + (fScaleT * (nY1 - nY0-1))*tile0.fShiftScaleT;
703     }
704     else
705     {
706         t0v1 = t0v0 + (fScaleT * (nY1 - nY0))*tile0.fShiftScaleT;
707     }
708
709     m_texRectTex1UV[0].v = t0v0/heightDiv;
710     m_texRectTex1UV[1].v = t0v1/heightDiv;
711     if( accurate && !tile0.bMirrorT && RemapTextureCoordinate(t0v0, t0v1, tex0.m_dwTileHeight, tile0.dwMaskT, heightDiv, m_texRectTex1UV[0].v, m_texRectTex1UV[1].v) )
712         SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
713     
714     COLOR speColor = PostProcessSpecularColor();
715     COLOR difColor;
716     if( colorFlag )
717         difColor = PostProcessDiffuseColor(diffuseColor);
718     else
719         //difColor = PostProcessDiffuseColor(0);
720         difColor = PostProcessDiffuseColor(gRDP.primitiveColor);
721
722     g_texRectTVtx[0].x = ViewPortTranslatei_x(nX0);
723     g_texRectTVtx[0].y = ViewPortTranslatei_y(nY0);
724     g_texRectTVtx[0].dcDiffuse = difColor;
725     g_texRectTVtx[0].dcSpecular = speColor;
726
727     g_texRectTVtx[1].x = ViewPortTranslatei_x(nX1);
728     g_texRectTVtx[1].y = ViewPortTranslatei_y(nY0);
729     g_texRectTVtx[1].dcDiffuse = difColor;
730     g_texRectTVtx[1].dcSpecular = speColor;
731
732     g_texRectTVtx[2].x = ViewPortTranslatei_x(nX1);
733     g_texRectTVtx[2].y = ViewPortTranslatei_y(nY1);
734     g_texRectTVtx[2].dcDiffuse = difColor;
735     g_texRectTVtx[2].dcSpecular = speColor;
736
737     g_texRectTVtx[3].x = ViewPortTranslatei_x(nX0);
738     g_texRectTVtx[3].y = ViewPortTranslatei_y(nY1);
739     g_texRectTVtx[3].dcDiffuse = difColor;
740     g_texRectTVtx[3].dcSpecular = speColor;
741
742     float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
743
744     g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = depth;
745     g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = 1;
746
747     if( IsTexel1Enable() )
748     {
749         RenderTexture &tex1 = g_textures[(gRSP.curTile+1)&7];
750         Tile &tile1 = gRDP.tiles[(gRSP.curTile+1)&7];
751
752         widthDiv = tex1.m_fTexWidth;
753         heightDiv = tex1.m_fTexHeight;
754         //if( tile1.dwMaskS == 0 )  widthDiv = tile1.dwWidth;
755         //if( tile1.dwMaskT == 0 )  heightDiv = tile1.dwHeight;
756
757         float t0u0 = fS0 * tile1.fShiftScaleS -tile1.fhilite_sl;
758         float t0v0 = fT0 * tile1.fShiftScaleT -tile1.fhilite_tl;
759         float t0u1;
760         float t0v1;
761         if ( accurate && gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY)
762         {
763             t0u1 = t0u0 + (fScaleS * (nX1 - nX0 - 1))*tile1.fShiftScaleS;
764             t0v1 = t0v0 + (fScaleT * (nY1 - nY0 - 1))*tile1.fShiftScaleT;
765         }
766         else
767         {
768             t0u1 = t0u0 + (fScaleS * (nX1 - nX0))*tile1.fShiftScaleS;
769             t0v1 = t0v0 + (fScaleT * (nY1 - nY0))*tile1.fShiftScaleT;
770         }
771
772         if( status.UseLargerTile[1] )
773         {
774             m_texRectTex2UV[0].u = (t0u0+status.LargerTileRealLeft[1])/widthDiv;
775             m_texRectTex2UV[1].u = (t0u1+status.LargerTileRealLeft[1])/widthDiv;
776         }
777         else
778         {
779             m_texRectTex2UV[0].u = t0u0/widthDiv;
780             m_texRectTex2UV[1].u = t0u1/widthDiv;
781             if( accurate && !tile1.bMirrorS && RemapTextureCoordinate(t0u0, t0u1, tex1.m_dwTileWidth, tile1.dwMaskS, widthDiv, m_texRectTex2UV[0].u, m_texRectTex2UV[1].u) )
782                 SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, (gRSP.curTile+1)&7);
783         }
784
785         m_texRectTex2UV[0].v = t0v0/heightDiv;
786         m_texRectTex2UV[1].v = t0v1/heightDiv;
787
788         if( accurate && !tile1.bMirrorT && RemapTextureCoordinate(t0v0, t0v1, tex1.m_dwTileHeight, tile1.dwMaskT, heightDiv, m_texRectTex2UV[0].v, m_texRectTex2UV[1].v) )
789             SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, (gRSP.curTile+1)&7);
790
791         SetVertexTextureUVCoord(g_texRectTVtx[0], m_texRectTex1UV[0].u, m_texRectTex1UV[0].v, m_texRectTex2UV[0].u, m_texRectTex2UV[0].v);
792         SetVertexTextureUVCoord(g_texRectTVtx[1], m_texRectTex1UV[1].u, m_texRectTex1UV[0].v, m_texRectTex2UV[1].u, m_texRectTex2UV[0].v);
793         SetVertexTextureUVCoord(g_texRectTVtx[2], m_texRectTex1UV[1].u, m_texRectTex1UV[1].v, m_texRectTex2UV[1].u, m_texRectTex2UV[1].v);
794         SetVertexTextureUVCoord(g_texRectTVtx[3], m_texRectTex1UV[0].u, m_texRectTex1UV[1].v, m_texRectTex2UV[0].u, m_texRectTex2UV[1].v);
795     }
796     else
797     {
798         SetVertexTextureUVCoord(g_texRectTVtx[0], m_texRectTex1UV[0].u, m_texRectTex1UV[0].v);
799         SetVertexTextureUVCoord(g_texRectTVtx[1], m_texRectTex1UV[1].u, m_texRectTex1UV[0].v);
800         SetVertexTextureUVCoord(g_texRectTVtx[2], m_texRectTex1UV[1].u, m_texRectTex1UV[1].v);
801         SetVertexTextureUVCoord(g_texRectTVtx[3], m_texRectTex1UV[0].u, m_texRectTex1UV[1].v);
802     }
803
804
805     bool res;
806     TurnFogOnOff(false);
807     if( TileUFlags[gRSP.curTile]==TEXTURE_UV_FLAG_CLAMP && TileVFlags[gRSP.curTile]==TEXTURE_UV_FLAG_CLAMP && options.forceTextureFilter == FORCE_DEFAULT_FILTER )
808     {
809         TextureFilter dwFilter = m_dwMagFilter;
810         m_dwMagFilter = m_dwMinFilter = FILTER_LINEAR;
811         ApplyTextureFilter();
812         ApplyRDPScissor();
813         res = RenderTexRect();
814         m_dwMagFilter = m_dwMinFilter = dwFilter;
815         ApplyTextureFilter();
816     }
817     else if( fScaleS >= 1 && fScaleT >= 1 && options.forceTextureFilter == FORCE_DEFAULT_FILTER )
818     {
819         TextureFilter dwFilter = m_dwMagFilter;
820         m_dwMagFilter = m_dwMinFilter = FILTER_POINT;
821         ApplyTextureFilter();
822         ApplyRDPScissor();
823         res = RenderTexRect();
824         m_dwMagFilter = m_dwMinFilter = dwFilter;
825         ApplyTextureFilter();
826     }
827     else
828     {
829         ApplyRDPScissor();
830         res = RenderTexRect();
831     }
832     TurnFogOnOff(gRSP.bFogEnabled);
833
834     if( gRDP.otherMode.cycle_type  >= CYCLE_TYPE_COPY || !gRDP.otherMode.z_cmp  )
835     {
836         ZBufferEnable(gRSP.bZBufferEnabled);
837     }
838
839     DEBUGGER_PAUSE_AT_COND_AND_DUMP_COUNT_N((eventToPause == NEXT_FLUSH_TRI || eventToPause == NEXT_TEXTRECT), {
840         DebuggerAppendMsg("TexRect: tile=%d, X0=%d, Y0=%d, X1=%d, Y1=%d,\nfS0=%f, fT0=%f, ScaleS=%f, ScaleT=%f\n",
841             gRSP.curTile, nX0, nY0, nX1, nY1, fS0, fT0, fScaleS, fScaleT);
842         DebuggerAppendMsg("       : x0=%f, y0=%f, x1=%f, y1=%f\n",  g_texRectTVtx[0].x, g_texRectTVtx[0].y, g_texRectTVtx[2].x, g_texRectTVtx[2].y);
843         DebuggerAppendMsg("   Tex0: u0=%f, v0=%f, u1=%f, v1=%f\n",  m_texRectTex1UV[0].u, m_texRectTex1UV[0].v, m_texRectTex1UV[1].u, m_texRectTex1UV[1].v);
844         if( IsTexel1Enable() )
845         {
846             DebuggerAppendMsg("   Tex1: u0=%f, v0=%f, u1=%f, v1=%f\n",  m_texRectTex2UV[0].u, m_texRectTex2UV[0].v, m_texRectTex2UV[1].u, m_texRectTex2UV[1].v);
847         }
848         DebuggerAppendMsg("color=%08X, %08X\n", g_texRectTVtx[0].dcDiffuse, g_texRectTVtx[0].dcSpecular);
849         DebuggerAppendMsg("Pause after TexRect\n");
850         if( logCombiners ) m_pColorCombiner->DisplayMuxString();
851     });
852
853     return res;
854 }
855
856
857 bool CRender::TexRectFlip(int nX0, int nY0, int nX1, int nY1, float fS0, float fT0, float fS1, float fT1)
858 {
859     LOG_UCODE("TexRectFlip: X0=%d, Y0=%d, X1=%d, Y1=%d,\n\t\tfS0=%f, fT0=%f, fS1=%f, fT1=%f ",
860             nX0, nY0, nX1, nY1, fS0, fT0, fS1, fT1);
861
862     if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )   
863     {
864         status.bFrameBufferIsDrawn = true;
865         status.bFrameBufferDrawnByTriangles = true;
866     }
867
868     PrepareTextures();
869
870     // Save ZBuffer state
871     m_savedZBufferFlag = gRSP.bZBufferEnabled;
872     if( gRDP.otherMode.depth_source == 0 )  ZBufferEnable( FALSE );
873
874     float widthDiv = g_textures[gRSP.curTile].m_fTexWidth;
875     float heightDiv = g_textures[gRSP.curTile].m_fTexHeight;
876
877     //Tile &tile0 = gRDP.tiles[gRSP.curTile];
878
879     float t0u0 = fS0 / widthDiv;
880     float t0v0 = fT0 / heightDiv;
881
882     float t0u1 = (fS1 - fS0)/ widthDiv + t0u0;
883     float t0v1 = (fT1 - fT0)/ heightDiv + t0v0;
884
885     float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
886
887     if( t0u0 >= 0 && t0u1 <= 1 && t0u1 >= t0u0 ) SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
888     if( t0v0 >= 0 && t0v1 <= 1 && t0v1 >= t0v0 ) SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
889
890     SetCombinerAndBlender();
891
892     COLOR speColor = PostProcessSpecularColor();
893     COLOR difColor = PostProcessDiffuseColor(gRDP.primitiveColor);
894
895     // Same as TexRect, but with texcoords 0,2 swapped
896     g_texRectTVtx[0].x = ViewPortTranslatei_x(nX0);
897     g_texRectTVtx[0].y = ViewPortTranslatei_y(nY0);
898     g_texRectTVtx[0].dcDiffuse = difColor;
899     g_texRectTVtx[0].dcSpecular = speColor;
900
901     g_texRectTVtx[1].x = ViewPortTranslatei_x(nX1);
902     g_texRectTVtx[1].y = ViewPortTranslatei_y(nY0);
903     g_texRectTVtx[1].dcDiffuse = difColor;
904     g_texRectTVtx[1].dcSpecular = speColor;
905
906     g_texRectTVtx[2].x = ViewPortTranslatei_x(nX1);
907     g_texRectTVtx[2].y = ViewPortTranslatei_y(nY1);
908     g_texRectTVtx[2].dcDiffuse = difColor;
909     g_texRectTVtx[2].dcSpecular = speColor;
910
911     g_texRectTVtx[3].x = ViewPortTranslatei_x(nX0);
912     g_texRectTVtx[3].y = ViewPortTranslatei_y(nY1);
913     g_texRectTVtx[3].dcDiffuse = difColor;
914     g_texRectTVtx[3].dcSpecular = speColor;
915
916     g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = depth;
917     g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = 1.0f;
918     
919     SetVertexTextureUVCoord(g_texRectTVtx[0], t0u0, t0v0);
920     SetVertexTextureUVCoord(g_texRectTVtx[1], t0u0, t0v1);
921     SetVertexTextureUVCoord(g_texRectTVtx[2], t0u1, t0v1);
922     SetVertexTextureUVCoord(g_texRectTVtx[3], t0u1, t0v0);
923
924     TurnFogOnOff(false);
925     ApplyRDPScissor();
926     bool res = RenderTexRect();
927
928     TurnFogOnOff(gRSP.bFogEnabled);
929
930     // Restore state
931     ZBufferEnable( m_savedZBufferFlag );
932
933     DEBUGGER_PAUSE_AT_COND_AND_DUMP_COUNT_N((eventToPause == NEXT_FLUSH_TRI || eventToPause == NEXT_TEXTRECT), {
934         DebuggerAppendMsg("TexRectFlip: tile=%d, X0=%d, Y0=%d, X1=%d, Y1=%d,\nfS0=%f, fT0=%f, nfS1=%f, fT1=%f\n",
935             gRSP.curTile, nX0, nY0, nX1, nY1, fS0, fT0, fS1, fT1);
936         DebuggerAppendMsg("       : x0=%f, y0=%f, x1=%f, y1=%f\n",  g_texRectTVtx[0].x, g_texRectTVtx[0].y, g_texRectTVtx[2].x, g_texRectTVtx[2].y);
937         DebuggerAppendMsg("   Tex0: u0=%f, v0=%f, u1=%f, v1=%f\n",  g_texRectTVtx[0].tcord[0].u, g_texRectTVtx[0].tcord[0].v, g_texRectTVtx[2].tcord[0].u, g_texRectTVtx[2].tcord[0].v);
938         TRACE0("Pause after TexRectFlip\n");
939         if( logCombiners ) m_pColorCombiner->DisplayMuxString();
940     });
941
942     return res;
943 }
944
945
946 void CRender::StartDrawSimple2DTexture(float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, COLOR dif, COLOR spe, float z, float rhw)
947 {
948     g_texRectTVtx[0].x = ViewPortTranslatei_x(x0);  // << Error here, shouldn't divid by 4
949     g_texRectTVtx[0].y = ViewPortTranslatei_y(y0);
950     g_texRectTVtx[0].dcDiffuse = dif;
951     g_texRectTVtx[0].dcSpecular = spe;
952     g_texRectTVtx[0].tcord[0].u = u0;
953     g_texRectTVtx[0].tcord[0].v = v0;
954
955
956     g_texRectTVtx[1].x = ViewPortTranslatei_x(x1);
957     g_texRectTVtx[1].y = ViewPortTranslatei_y(y0);
958     g_texRectTVtx[1].dcDiffuse = dif;
959     g_texRectTVtx[1].dcSpecular = spe;
960     g_texRectTVtx[1].tcord[0].u = u1;
961     g_texRectTVtx[1].tcord[0].v = v0;
962
963     g_texRectTVtx[2].x = ViewPortTranslatei_x(x1);
964     g_texRectTVtx[2].y = ViewPortTranslatei_y(y1);
965     g_texRectTVtx[2].dcDiffuse = dif;
966     g_texRectTVtx[2].dcSpecular = spe;
967     g_texRectTVtx[2].tcord[0].u = u1;
968     g_texRectTVtx[2].tcord[0].v = v1;
969
970     g_texRectTVtx[3].x = ViewPortTranslatei_x(x0);
971     g_texRectTVtx[3].y = ViewPortTranslatei_y(y1);
972     g_texRectTVtx[3].dcDiffuse = dif;
973     g_texRectTVtx[3].dcSpecular = spe;
974     g_texRectTVtx[3].tcord[0].u = u0;
975     g_texRectTVtx[3].tcord[0].v = v1;
976
977     RenderTexture &txtr = g_textures[0];
978     if( txtr.pTextureEntry && txtr.pTextureEntry->txtrBufIdx > 0 )
979     {
980         RenderTextureInfo &info = gRenderTextureInfos[txtr.pTextureEntry->txtrBufIdx-1];
981         g_texRectTVtx[0].tcord[0].u *= info.scaleX;
982         g_texRectTVtx[0].tcord[0].v *= info.scaleY;
983         g_texRectTVtx[1].tcord[0].u *= info.scaleX;
984         g_texRectTVtx[1].tcord[0].v *= info.scaleY;
985         g_texRectTVtx[2].tcord[0].u *= info.scaleX;
986         g_texRectTVtx[2].tcord[0].v *= info.scaleY;
987         g_texRectTVtx[3].tcord[0].u *= info.scaleX;
988         g_texRectTVtx[3].tcord[0].v *= info.scaleY;
989     }
990
991     g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = z;
992     g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = rhw;
993 }
994
995 void CRender::StartDrawSimpleRect(int nX0, int nY0, int nX1, int nY1, uint32 dwColor, float depth, float rhw)
996 {
997     m_simpleRectVtx[0].x = ViewPortTranslatei_x(nX0);
998     m_simpleRectVtx[1].x = ViewPortTranslatei_x(nX1);
999     m_simpleRectVtx[0].y = ViewPortTranslatei_y(nY0);
1000     m_simpleRectVtx[1].y = ViewPortTranslatei_y(nY1);
1001 }
1002
1003 void CRender::SetAddressUAllStages(uint32 dwTile, TextureUVFlag dwFlag)
1004 {
1005 }
1006
1007 void CRender::SetAddressVAllStages(uint32 dwTile, TextureUVFlag dwFlag)
1008 {
1009 }
1010
1011 void CRender::SetAllTexelRepeatFlag()
1012 {
1013     if( IsTextureEnabled() )
1014     {
1015         if( IsTexel0Enable() || gRDP.otherMode.cycle_type  == CYCLE_TYPE_COPY )
1016             SetTexelRepeatFlags(gRSP.curTile);
1017         if( IsTexel1Enable() )
1018             SetTexelRepeatFlags((gRSP.curTile+1)&7);
1019     }
1020 }
1021
1022
1023 void CRender::SetTexelRepeatFlags(uint32 dwTile)
1024 {
1025     Tile &tile = gRDP.tiles[dwTile];
1026
1027     if( tile.bForceClampS )
1028         SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
1029     else if( tile.bForceWrapS )
1030             SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
1031     else if( tile.dwMaskS == 0 || tile.bClampS )
1032     {
1033         if( gRDP.otherMode.cycle_type  >= CYCLE_TYPE_COPY )
1034             SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile);  // Can not clamp in COPY/FILL mode
1035         else
1036             SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
1037     }
1038     else if (tile.bMirrorS )
1039         SetTextureUFlag(TEXTURE_UV_FLAG_MIRROR, dwTile);
1040     else                                
1041         SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
1042     
1043     if( tile.bForceClampT )
1044         SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
1045     else if( tile.bForceWrapT )
1046         SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
1047     else if( tile.dwMaskT == 0 || tile.bClampT)
1048     {
1049         if( gRDP.otherMode.cycle_type  >= CYCLE_TYPE_COPY )
1050             SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile);  // Can not clamp in COPY/FILL mode
1051         else
1052             SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
1053     }
1054     else if (tile.bMirrorT )
1055         SetTextureVFlag(TEXTURE_UV_FLAG_MIRROR, dwTile);
1056     else                                
1057         SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
1058 }
1059
1060 void CRender::Initialize(void)
1061 {
1062     ClearDeviceObjects();
1063     InitDeviceObjects();
1064 }
1065
1066 void CRender::CleanUp(void)
1067 {
1068     m_pColorCombiner->CleanUp();
1069     ClearDeviceObjects();
1070 }
1071
1072 void myVec3Transform(float *vecout, float *vecin, float* m)
1073 {
1074     float w = m[3]*vecin[0]+m[7]*vecin[1]+m[11]*vecin[2]+m[15];
1075     vecout[0] = (m[0]*vecin[0]+m[4]*vecin[1]+m[8]*vecin[2]+m[12])/w;
1076     vecout[1] = (m[1]*vecin[0]+m[5]*vecin[1]+m[9]*vecin[2]+m[13])/w;
1077     vecout[2] = (m[2]*vecin[0]+m[6]*vecin[1]+m[10]*vecin[2]+m[14])/w;
1078 }
1079
1080 void CRender::SetTextureEnableAndScale(int dwTile, bool bEnable, float fScaleX, float fScaleY)
1081 {
1082     gRSP.bTextureEnabled = bEnable;
1083
1084     if( bEnable )
1085     {
1086         if( gRSP.curTile != (unsigned int)dwTile )
1087             gRDP.textureIsChanged = true;
1088
1089         gRSP.curTile = dwTile;
1090
1091         gRSP.fTexScaleX = fScaleX;
1092         gRSP.fTexScaleY = fScaleY;
1093
1094         if( fScaleX == 0 || fScaleY == 0 )
1095         {
1096             gRSP.fTexScaleX = 1/32.0f;
1097             gRSP.fTexScaleY = 1/32.0f;
1098         }
1099     }
1100 }
1101
1102 void CRender::SetFogFlagForNegativeW()
1103 {
1104     if( !gRSP.bFogEnabled ) return;
1105
1106     m_bFogStateSave = gRSP.bFogEnabled;
1107
1108     bool flag=gRSP.bFogEnabled;
1109     
1110     for (uint32 i = 0; i < gRSP.numVertices; i++) 
1111     {
1112         if( g_vtxBuffer[i].rhw < 0 )
1113             flag = FALSE;
1114     }
1115
1116     TurnFogOnOff(flag);
1117 }
1118
1119 void CRender::RestoreFogFlag()
1120 {
1121     if( !gRSP.bFogEnabled ) return;
1122     TurnFogOnOff(m_bFogStateSave);
1123 }
1124
1125 void CRender::SetViewport(int nLeft, int nTop, int nRight, int nBottom, int maxZ)
1126 {
1127     if( status.bHandleN64RenderTexture )
1128         return;
1129
1130     static float MultX=0, MultY=0;
1131
1132     if( gRSP.nVPLeftN == nLeft && gRSP.nVPTopN == nTop &&
1133         gRSP.nVPRightN == nRight && gRSP.nVPBottomN == nBottom &&
1134         MultX==windowSetting.fMultX && MultY==windowSetting.fMultY)
1135     {
1136         // no changes
1137         return;
1138     }
1139
1140     MultX=windowSetting.fMultX;
1141     MultY=windowSetting.fMultY;
1142
1143     gRSP.maxZ = maxZ;
1144     gRSP.nVPLeftN = nLeft;
1145     gRSP.nVPTopN = nTop;
1146     gRSP.nVPRightN = nRight;
1147     gRSP.nVPBottomN = nBottom;
1148     gRSP.nVPWidthN = nRight - nLeft + 1;
1149     gRSP.nVPHeightN = nBottom - nTop + 1;
1150
1151     UpdateClipRectangle();
1152     SetViewportRender();
1153
1154     LOG_UCODE("SetViewport (%d,%d - %d,%d)",gRSP.nVPLeftN, gRSP.nVPTopN, gRSP.nVPRightN, gRSP.nVPBottomN);
1155 }
1156
1157 extern bool bHalfTxtScale;
1158
1159 bool CRender::DrawTriangles()
1160 {
1161     if( !status.bCIBufferIsRendered ) g_pFrameBufferManager->ActiveTextureBuffer();
1162
1163     DEBUGGER_ONLY_IF( (!debuggerEnableZBuffer), {ZBufferEnable( FALSE );} );
1164
1165     if( status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE )
1166     {
1167         status.bVIOriginIsUpdated=false;
1168         CGraphicsContext::Get()->UpdateFrame();
1169         DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG,{DebuggerAppendMsg("Screen Update at 1st triangle");});
1170     }
1171
1172     // Hack for Pilotwings 64 (U) [!].v64
1173     static bool skipNext=false;
1174     if( options.enableHackForGames == HACK_FOR_PILOT_WINGS )
1175     {
1176         if( IsUsedAsDI(g_CI.dwAddr) && gRDP.otherMode.z_cmp+gRDP.otherMode.z_upd > 0 )
1177         {
1178             TRACE0("Warning: using Flushtris to write Zbuffer" );
1179             gRSP.numVertices = 0;
1180             gRSP.maxVertexID = 0;
1181             skipNext = true;
1182             return true;
1183         }
1184         else if( skipNext )
1185         {
1186             skipNext = false;
1187             gRSP.numVertices = 0;
1188             gRSP.maxVertexID = 0;
1189             return true;
1190         }   
1191     }
1192
1193     if( status.bN64IsDrawingTextureBuffer && frameBufferOptions.bIgnore )
1194     {
1195         gRSP.numVertices = 0;
1196         gRSP.maxVertexID = 0;
1197         return true;
1198     }
1199
1200     extern bool bConkerHideShadow;
1201     if( options.enableHackForGames == HACK_FOR_CONKER && bConkerHideShadow )
1202     {
1203         gRSP.numVertices = 0;
1204         gRSP.maxVertexID = 0;
1205         return true;
1206     }
1207
1208     if( IsUsedAsDI(g_CI.dwAddr) && !status.bHandleN64RenderTexture )
1209     {
1210         status.bFrameBufferIsDrawn = true;
1211     }
1212
1213     /*
1214     if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == TXT_SIZE_8b ) 
1215     {
1216         gRSP.numVertices = 0;
1217         gRSP.maxVertexID = 0;
1218         return true;
1219     }
1220     */
1221
1222     if (gRSP.numVertices == 0)  return true;
1223     if( status.bHandleN64RenderTexture )
1224     {
1225         g_pRenderTextureInfo->maxUsedHeight = g_pRenderTextureInfo->N64Height;
1226         if( !status.bDirectWriteIntoRDRAM ) 
1227         {
1228             status.bFrameBufferIsDrawn = true;
1229             status.bFrameBufferDrawnByTriangles = true;
1230         }
1231     }
1232
1233     if( !gRDP.bFogEnableInBlender && gRSP.bFogEnabled )
1234     {
1235         TurnFogOnOff(false);
1236     }
1237
1238     for( int t=0; t<2; t++ )
1239     {
1240         float halfscaleS = 1;
1241
1242         // This will get rid of the thin black lines
1243         if( t==0 && !(m_pColorCombiner->m_bTex0Enabled) ) 
1244             continue;
1245         else
1246         {
1247             if( ( gRDP.tiles[gRSP.curTile].dwSize == TXT_SIZE_32b && options.enableHackForGames == HACK_FOR_RUMBLE ) ||
1248                 (bHalfTxtScale && g_curRomInfo.bTextureScaleHack ) ||
1249                 (options.enableHackForGames == HACK_FOR_POLARISSNOCROSS && gRDP.tiles[7].dwFormat == TXT_FMT_CI && gRDP.tiles[7].dwSize == TXT_SIZE_8b 
1250                 && gRDP.tiles[0].dwFormat == TXT_FMT_CI && gRDP.tiles[0].dwSize == TXT_SIZE_8b && gRSP.curTile == 0 ))
1251             {
1252                 halfscaleS = 0.5;
1253             }
1254         }
1255
1256         if( t==1 && !(m_pColorCombiner->m_bTex1Enabled) )
1257             break;
1258
1259         if( halfscaleS < 1 )
1260         {
1261             for( uint32 i=0; i<gRSP.numVertices; i++ )
1262             {
1263                 if( t == 0 )
1264                 {
1265                     g_vtxBuffer[i].tcord[t].u += gRSP.tex0OffsetX;
1266                     g_vtxBuffer[i].tcord[t].u /= 2;
1267                     g_vtxBuffer[i].tcord[t].u -= gRSP.tex0OffsetX;
1268                     g_vtxBuffer[i].tcord[t].v += gRSP.tex0OffsetY;
1269                     g_vtxBuffer[i].tcord[t].v /= 2;
1270                     g_vtxBuffer[i].tcord[t].v -= gRSP.tex0OffsetY;
1271                 }
1272                 else
1273                 {
1274                     g_vtxBuffer[i].tcord[t].u += gRSP.tex1OffsetX;
1275                     g_vtxBuffer[i].tcord[t].u /= 2;
1276                     g_vtxBuffer[i].tcord[t].u -= gRSP.tex1OffsetX;
1277                     g_vtxBuffer[i].tcord[t].v += gRSP.tex1OffsetY;
1278                     g_vtxBuffer[i].tcord[t].v /= 2;
1279                     g_vtxBuffer[i].tcord[t].v -= gRSP.tex1OffsetY;
1280                 }
1281             }
1282         }
1283
1284         /*
1285         // The code here is disabled because it could cause incorrect texture repeating flag 
1286         // for later DrawTriangles
1287         bool clampS=true;
1288         bool clampT=true;
1289
1290         for( uint32 i=0; i<gRSP.numVertices; i++ )
1291         {
1292             float w = CDeviceBuilder::GetGeneralDeviceType() == OGL_DEVICE ? g_vtxProjected5[i][3] : g_vtxBuffer[i].rhw; 
1293             if( w < 0 || g_vtxBuffer[i].tcord[t].u > 1.0 || g_vtxBuffer[i].tcord[t].u < 0.0  )
1294             {
1295                 clampS = false;
1296                 break;
1297             }
1298         }
1299
1300         for( uint32 i=0; i<gRSP.numVertices; i++ )
1301         {
1302             float w = CDeviceBuilder::GetGeneralDeviceType() == OGL_DEVICE ? g_vtxProjected5[i][3] : g_vtxBuffer[i].rhw; 
1303             if( w < 0 || g_vtxBuffer[i].tcord[t].v > 1.0 || g_vtxBuffer[i].tcord[t].v < 0.0  )
1304             {
1305                 clampT = false;
1306                 break;
1307             }
1308         }
1309
1310         if( clampS )
1311         {
1312             SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile+t);
1313         }
1314         if( clampT )
1315         {
1316             SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile+t);
1317         }
1318         */
1319     }
1320
1321     if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == TXT_SIZE_8b )
1322     {
1323         ZBufferEnable(FALSE);
1324     }
1325
1326     ApplyScissorWithClipRatio();
1327
1328     if( g_curRomInfo.bZHack )
1329     {
1330         extern void HackZAll();
1331         HackZAll();
1332     }
1333
1334     bool res = RenderFlushTris();
1335     g_clippedVtxCount = 0;
1336
1337     LOG_UCODE("DrawTriangles: Draw %d Triangles", gRSP.numVertices/3);
1338     
1339     gRSP.numVertices = 0;   // Reset index
1340     gRSP.maxVertexID = 0;
1341
1342     DEBUGGER_PAUSE_AND_DUMP_COUNT_N(NEXT_FLUSH_TRI, {
1343         TRACE0("Pause after DrawTriangles\n");
1344         if( logCombiners ) m_pColorCombiner->DisplayMuxString();
1345     });
1346
1347     if( !gRDP.bFogEnableInBlender && gRSP.bFogEnabled )
1348     {
1349         TurnFogOnOff(true);
1350     }
1351
1352     return res;
1353 }
1354
1355 inline int ReverseCITableLookup(uint32 *pTable, int size, uint32 val)
1356 {
1357     for( int i=0; i<size; i++)
1358     {
1359         if( pTable[i] == val )
1360             return i;
1361     }
1362
1363     TRACE0("Cannot find value in CI table");
1364     return 0;
1365 }
1366
1367 bool SaveCITextureToFile(TxtrCacheEntry &entry, char *filename, bool bShow, bool bWholeTexture )
1368 {
1369     if( !( (gRDP.otherMode.text_tlut>=2 || entry.ti.Format == TXT_FMT_CI || entry.ti.Format == TXT_FMT_RGBA) && entry.ti.Size <= TXT_SIZE_8b ) )
1370     {
1371         // No a CI texture
1372         return false;
1373     }
1374     if ( entry.ti.TLutFmt != TLUT_FMT_RGBA16 && entry.ti.TLutFmt != TLUT_FMT_IA16 )
1375     {
1376         TRACE0("Invalid Tlut format");
1377         return false;
1378     }
1379
1380     if( !entry.pTexture )
1381     {
1382         TRACE0("Null texture");
1383         return false;
1384     }
1385
1386     uint32 *pTable = NULL;
1387     int tableSize;
1388     uint16 * pPal = (uint16 *)entry.ti.PalAddress;
1389
1390     // Create the pallette table
1391     if( entry.ti.Size == TXT_SIZE_4b )
1392     {
1393         // 4-bit table
1394         tableSize = 16;
1395         pTable = new uint32[16];
1396
1397         for( int i=0; i<16; i++ )
1398         {
1399             pTable[i] = entry.ti.TLutFmt == TLUT_FMT_RGBA16 ? Convert555ToRGBA(pPal[i^1]) : ConvertIA16ToRGBA(pPal[i^1]);
1400         }
1401     }
1402     else
1403     {
1404         // 8-bit table
1405         tableSize = 256;
1406         pTable = new uint32[256];
1407
1408         for( int i=0; i<256; i++ )
1409         {
1410             pTable[i] = entry.ti.TLutFmt == TLUT_FMT_RGBA16 ? Convert555ToRGBA(pPal[i^1]) : ConvertIA16ToRGBA(pPal[i^1]);
1411         }
1412     }
1413
1414     // Reversely convert current texture to indexed textures
1415     CTexture &texture = *entry.pTexture;
1416     //int width = bWholeTexture ? texture.m_dwCreatedTextureWidth : texture.m_dwWidth;
1417     //int height = bWholeTexture ? texture.m_dwCreatedTextureHeight : texture.m_dwHeight;
1418     int width = bWholeTexture ? texture.m_dwCreatedTextureWidth : entry.ti.WidthToLoad;
1419     int height = bWholeTexture ? texture.m_dwCreatedTextureHeight : entry.ti.HeightToLoad;
1420     int bufSizePerLine = (((((width << entry.ti.Size) + 1 ) >> 1)+3) >> 2)*4;   // pad to 32bit boundary
1421     int bufSize = bufSizePerLine*height;
1422     unsigned char *pbuf = new unsigned char[bufSize];
1423
1424     DrawInfo srcInfo;
1425     if( texture.StartUpdate(&srcInfo) )
1426     {
1427         int idx = 0;
1428         for( int i=height-1; i>=0; i--)
1429         {
1430             uint32 *pSrc = (uint32*)((unsigned char*)srcInfo.lpSurface+srcInfo.lPitch * i);
1431             for( int j=0; j<width; j++)
1432             {
1433                 int val = ReverseCITableLookup(pTable, tableSize, *pSrc);
1434                 pSrc++;
1435
1436                 if( entry.ti.Size == TXT_SIZE_4b )
1437                 {
1438                     // 4 bits
1439                     if( idx%2 )
1440                     {
1441                         // 1
1442                         pbuf[idx>>1] = (pbuf[idx>>1]<<4) | val;
1443                         idx++;
1444                     }
1445                     else
1446                     {
1447                         // 0
1448                         pbuf[idx>>1] = (unsigned char)val;
1449                         idx++;
1450                     }
1451                 }
1452                 else
1453                 {
1454                     // 8 bits
1455                     pbuf[idx++] = (unsigned char)val;
1456                 }
1457             }
1458             if( entry.ti.Size == TXT_SIZE_4b )
1459             {
1460                 if( idx%8 ) idx = (idx/8+1)*8;
1461             }
1462             else
1463             {
1464                 if( idx%4 ) idx = (idx/4+1)*4;
1465             }
1466         }
1467
1468
1469         texture.EndUpdate(&srcInfo);
1470     }
1471
1472     // Create BMP color indexed file
1473     if( strcasecmp(right(filename,4),".bmp") != 0 )
1474         strcat(filename,".bmp");
1475
1476     BITMAPFILEHEADER fileHeader;
1477     BITMAPINFOHEADER infoHeader;
1478
1479     infoHeader.biSize = sizeof( BITMAPINFOHEADER );
1480     infoHeader.biWidth = width;
1481     infoHeader.biHeight = height;
1482     infoHeader.biPlanes = 1;
1483     infoHeader.biBitCount = entry.ti.Size == TXT_SIZE_4b ? 4 : 8;
1484     infoHeader.biCompression = BI_RGB;
1485     infoHeader.biSizeImage = bufSize;
1486     infoHeader.biXPelsPerMeter = 0;
1487     infoHeader.biYPelsPerMeter = 0;
1488     infoHeader.biClrUsed = 0;
1489     infoHeader.biClrImportant = 0;
1490
1491     fileHeader.bfType = 19778;
1492     fileHeader.bfSize = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + infoHeader.biSizeImage + tableSize*4;
1493     fileHeader.bfReserved1 = fileHeader.bfReserved2 = 0;
1494     fileHeader.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + tableSize*4;
1495
1496
1497     FILE *f;
1498     f = fopen(filename, "wb");
1499     if(f != NULL)
1500     {
1501         if (fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f) != 1 ||
1502             fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f) != 1 ||
1503             fwrite(pTable, tableSize*4, 1, f) != 1 ||
1504             fwrite(pbuf, infoHeader.biSizeImage, 1, f) != 1)
1505             printf("failed to write out texture data to image file '%s'", filename);
1506        
1507         fclose(f);
1508     }
1509     else
1510     {
1511         // Do something
1512         TRACE1("Fail to create file %s", filename);
1513     }
1514
1515     // Clean up
1516     delete [] pTable;
1517     delete [] pbuf;
1518
1519     return true;
1520 }
1521
1522 void CRender::SaveTextureToFile(CTexture &texture, char *filename, TextureChannel channel, bool bShow, bool bWholeTexture, int width, int height)
1523 {
1524     if( width < 0 || height < 0 )
1525     {
1526         width = bWholeTexture ? texture.m_dwCreatedTextureWidth : texture.m_dwWidth;
1527         height = bWholeTexture ? texture.m_dwCreatedTextureHeight : texture.m_dwHeight;
1528     }
1529
1530     unsigned char *pbuf = new unsigned char[width*height* (channel == TXT_RGBA ? 4 : 3)];
1531     if( pbuf )
1532     {
1533         DrawInfo srcInfo;   
1534         if( texture.StartUpdate(&srcInfo) )
1535         {
1536             if( channel == TXT_RGBA )
1537             {
1538                 uint32 *pbuf2 = (uint32*)pbuf;
1539                 for( int i=height-1; i>=0; i--)
1540                 {
1541                     uint32 *pSrc = (uint32*)((unsigned char*)srcInfo.lpSurface+srcInfo.lPitch * i);
1542                     for( int j=0; j<width; j++)
1543                     {
1544                         *pbuf2++ = *pSrc++;
1545                     }
1546                 }
1547
1548                 if( SaveRGBABufferToPNGFile(filename, (unsigned char*)pbuf, width, height ) )
1549                 //if( SaveRGBABufferToPNGFile(filename, (unsigned char*)srcInfo.lpSurface, width, height, srcInfo.lPitch ) )
1550                 {
1551                 }
1552             }
1553             else
1554             {
1555                 unsigned char *pbuf2 = pbuf;
1556                 for( int i=height-1; i>=0; i--)
1557                 {
1558                     unsigned char *pSrc = (unsigned char*)srcInfo.lpSurface+srcInfo.lPitch * i;
1559                     for( int j=0; j<width; j++)
1560                     {
1561                         if( channel == TXT_ALPHA )
1562                         {
1563                             pbuf2[0] = pbuf2[1] = pbuf2[2] = pSrc[3];
1564                         }
1565                         else
1566                         {
1567                             pbuf2[0] = pSrc[0];
1568                             pbuf2[1] = pSrc[1];
1569                             pbuf2[2] = pSrc[2];
1570                         }
1571
1572                         pbuf2 += 3;
1573                         pSrc += 4;
1574                     }
1575                 }
1576
1577                 if( SaveRGBBufferToFile(filename, pbuf, width, height ) )
1578                 {
1579                 }
1580             }
1581             texture.EndUpdate(&srcInfo);
1582         }
1583         else
1584         {
1585             TRACE0("Cannot lock texture");
1586         }
1587         delete [] pbuf;
1588     }
1589     else
1590     {
1591         TRACE0("Out of memory");
1592     }
1593 }
1594
1595
1596 #ifdef DEBUGGER
1597 bool CRender::DrawTexture(int tex, TextureChannel channel)
1598 {
1599     if( g_textures[tex].m_pCTexture == NULL )
1600     {
1601         TRACE0("Can't draw null texture");
1602         return false;
1603     }
1604
1605     SaveTextureToFile(tex, channel, true);  // Save to file instead of draw to screen
1606     DebuggerAppendMsg("Texture %d (CurTile:%d): W=%f, H=%f, Real W=%d, H=%d", tex, gRSP.curTile, 
1607         g_textures[tex].m_fTexWidth, g_textures[tex].m_fTexHeight, g_textures[tex].m_dwTileWidth, g_textures[tex].m_dwTileHeight);
1608     DebuggerAppendMsg("X scale: %f, Y scale: %f, %s", gRSP.fTexScaleX, gRSP.fTexScaleY, gRSP.bTextureEnabled?"Enabled":"Disabled");
1609
1610     return true;
1611 }
1612
1613 void CRender::SaveTextureToFile(int tex, TextureChannel channel, bool bShow)
1614 {
1615     TxtrCacheEntry &entry = *(g_textures[tex].pTextureEntry);
1616
1617     CTexture *pBaseTexture = entry.pTexture;
1618     if( pBaseTexture == NULL )
1619     {
1620         TRACE0("Can't dump null texture");
1621         return;
1622     }
1623     CTexture *pEnhancedTexture = entry.pEnhancedTexture ? entry.pEnhancedTexture : entry.pTexture;
1624
1625     bool bInWhole = false;
1626     if( entry.ti.HeightToCreate == entry.ti.HeightToLoad && entry.ti.WidthToCreate == entry.ti.WidthToLoad 
1627         && pEnhancedTexture == pBaseTexture )
1628         bInWhole = true;
1629
1630     char filename[256];
1631     if( (gRDP.otherMode.text_tlut>=2 || g_textures[tex].pTextureEntry->ti.Format == TXT_FMT_CI || g_textures[tex].pTextureEntry->ti.Format == TXT_FMT_RGBA) && g_textures[tex].pTextureEntry->ti.Size <= TXT_SIZE_8b )
1632     {
1633         sprintf(filename, "\\%s#%08X#%d#%d_ci", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC, 
1634             g_textures[tex].pTextureEntry->ti.Format, 
1635             g_textures[tex].pTextureEntry->ti.Size);
1636         SaveCITextureToFile(*(g_textures[tex].pTextureEntry), filename, bShow && bInWhole, false);
1637
1638         sprintf(filename, "\\%s#%08X#%d#%d#%08X_ci", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC, 
1639             g_textures[tex].pTextureEntry->ti.Format, 
1640             g_textures[tex].pTextureEntry->ti.Size, g_textures[tex].pTextureEntry->dwPalCRC);
1641         SaveCITextureToFile(*(g_textures[tex].pTextureEntry), filename, false, false);
1642
1643         sprintf(filename, "\\%s#%08X#%d#%d#%08X_ciByRGBA", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC, 
1644             g_textures[tex].pTextureEntry->ti.Format, 
1645             g_textures[tex].pTextureEntry->ti.Size, g_textures[tex].pTextureEntry->dwPalCRC);
1646         SaveTextureToFile(*pBaseTexture, filename, TXT_RGBA, false, false, g_textures[tex].pTextureEntry->ti.WidthToLoad, g_textures[tex].pTextureEntry->ti.HeightToLoad);
1647
1648         DebuggerAppendMsg("Base texture is stored at: %s", filename);
1649
1650         if( !bInWhole && bShow )
1651         {
1652             sprintf(filename, "\\%s#%08X#%d#%d_ci_%s_debugger", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC, 
1653                 g_textures[tex].pTextureEntry->ti.Format, 
1654                 g_textures[tex].pTextureEntry->ti.Size, channel == TXT_ALPHA ? "a" : channel == TXT_RGBA ? "all" : "rgb");
1655             SaveTextureToFile(*pEnhancedTexture, filename, channel, true, true);
1656             DebuggerAppendMsg("Whole texture is stored at: %s", filename);
1657         }
1658     }
1659     else
1660     {
1661         sprintf(filename, "\\%s#%08X#%d#%d_%s", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC, 
1662             g_textures[tex].pTextureEntry->ti.Format, 
1663             g_textures[tex].pTextureEntry->ti.Size, channel == TXT_ALPHA ? "a" : channel == TXT_RGBA ? "all" : "rgb");
1664         SaveTextureToFile(*pBaseTexture, filename, channel, bShow && bInWhole, false,g_textures[tex].pTextureEntry->ti.WidthToLoad, g_textures[tex].pTextureEntry->ti.HeightToLoad);
1665         DebuggerAppendMsg("Base texture is stored at: %s", filename);
1666
1667         if( !bInWhole && bShow )
1668         {
1669             sprintf(filename, "\\%s#%08X#%d#%d_%s_debugger", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC, 
1670                 g_textures[tex].pTextureEntry->ti.Format, 
1671                 g_textures[tex].pTextureEntry->ti.Size, channel == TXT_ALPHA ? "a" : channel == TXT_RGBA ? "all" : "rgb");
1672             SaveTextureToFile(*pEnhancedTexture, filename, channel, true, true);
1673             DebuggerAppendMsg("Whole texture is stored at: %s", filename);
1674         }
1675     }
1676 }
1677 #endif
1678
1679 extern RenderTextureInfo gRenderTextureInfos[];
1680 void SetVertexTextureUVCoord(TexCord &dst, float s, float t, int tile, TxtrCacheEntry *pEntry)
1681 {
1682     RenderTexture &txtr = g_textures[tile];
1683     RenderTextureInfo &info = gRenderTextureInfos[pEntry->txtrBufIdx-1];
1684
1685     uint32 addrOffset = g_TI.dwAddr-info.CI_Info.dwAddr;
1686     uint32 extraTop = (addrOffset>>(info.CI_Info.dwSize-1)) /info.CI_Info.dwWidth;
1687     uint32 extraLeft = (addrOffset>>(info.CI_Info.dwSize-1))%info.CI_Info.dwWidth;
1688
1689     if( pEntry->txtrBufIdx > 0  )
1690     {
1691         // Loading from render_texture or back buffer
1692         s += (extraLeft+pEntry->ti.LeftToLoad)/txtr.m_fTexWidth;
1693         t += (extraTop+pEntry->ti.TopToLoad)/txtr.m_fTexHeight;
1694
1695         s *= info.scaleX;
1696         t *= info.scaleY;
1697     }
1698
1699     dst.u = s;
1700     dst.v = t;
1701 }
1702
1703 void CRender::SetVertexTextureUVCoord(TLITVERTEX &v, float fTex0S, float fTex0T)
1704 {
1705     RenderTexture &txtr = g_textures[0];
1706     if( txtr.pTextureEntry && txtr.pTextureEntry->txtrBufIdx > 0 )
1707     {
1708         ::SetVertexTextureUVCoord(v.tcord[0], fTex0S, fTex0T, 0, txtr.pTextureEntry);
1709     }
1710     else
1711     {
1712         v.tcord[0].u = fTex0S;
1713         v.tcord[0].v = fTex0T;
1714     }
1715 }
1716 void CRender::SetVertexTextureUVCoord(TLITVERTEX &v, float fTex0S, float fTex0T, float fTex1S, float fTex1T)
1717 {
1718     if( (options.enableHackForGames == HACK_FOR_ZELDA||options.enableHackForGames == HACK_FOR_ZELDA_MM) && m_Mux == 0x00262a60150c937fLL && gRSP.curTile == 0 )
1719     {
1720         // Hack for Zelda Sun
1721         Tile &t0 = gRDP.tiles[0];
1722         Tile &t1 = gRDP.tiles[1];
1723         if( t0.dwFormat == TXT_FMT_I && t0.dwSize == TXT_SIZE_8b && t0.dwWidth == 64 &&
1724             t1.dwFormat == TXT_FMT_I && t1.dwSize == TXT_SIZE_8b && t1.dwWidth == 64 &&
1725             t0.dwHeight == t1.dwHeight )
1726         {
1727             fTex0S /= 2;
1728             fTex0T /= 2;
1729             fTex1S /= 2;
1730             fTex1T /= 2;
1731         }
1732     }
1733
1734     RenderTexture &txtr0 = g_textures[0];
1735     if( txtr0.pTextureEntry && txtr0.pTextureEntry->txtrBufIdx > 0 )
1736     {
1737         ::SetVertexTextureUVCoord(v.tcord[0], fTex0S, fTex0T, 0, txtr0.pTextureEntry);
1738     }
1739     else
1740     {
1741         v.tcord[0].u = fTex0S;
1742         v.tcord[0].v = fTex0T;
1743     }
1744
1745     RenderTexture &txtr1 = g_textures[1];
1746     if( txtr1.pTextureEntry && txtr1.pTextureEntry->txtrBufIdx > 0 )
1747     {
1748         ::SetVertexTextureUVCoord(v.tcord[1], fTex1S, fTex1T, 1, txtr1.pTextureEntry);
1749     }
1750     else
1751     {
1752         v.tcord[1].u = fTex1S;
1753         v.tcord[1].v = fTex1T;
1754     }
1755 }
1756
1757 void CRender::SetClipRatio(uint32 type, uint32 w1)
1758 {
1759     bool modified = false;
1760     switch(type)
1761     {
1762     case RSP_MV_WORD_OFFSET_CLIP_RNX:
1763         LOG_UCODE("    RSP_MOVE_WORD_CLIP  NegX: %d", (int)(short)w1);
1764         if( gRSP.clip_ratio_negx != (short)w1 )
1765         {
1766             gRSP.clip_ratio_negx = (short)w1;
1767             modified = true;
1768         }
1769         break;
1770     case RSP_MV_WORD_OFFSET_CLIP_RNY:
1771         LOG_UCODE("    RSP_MOVE_WORD_CLIP  NegY: %d", (int)(short)w1);
1772         if( gRSP.clip_ratio_negy != (short)w1 )
1773         {
1774             gRSP.clip_ratio_negy = (short)w1;
1775             modified = true;
1776         }
1777         break;
1778     case RSP_MV_WORD_OFFSET_CLIP_RPX:
1779         LOG_UCODE("    RSP_MOVE_WORD_CLIP  PosX: %d", (int)(short)w1);
1780         if( gRSP.clip_ratio_posx != -(short)w1 )
1781         {
1782             gRSP.clip_ratio_posx = -(short)w1;
1783             modified = true;
1784         }
1785         break;
1786     case RSP_MV_WORD_OFFSET_CLIP_RPY:
1787         LOG_UCODE("    RSP_MOVE_WORD_CLIP  PosY: %d", (int)(short)w1);
1788         if( gRSP.clip_ratio_posy != -(short)w1 )
1789         {
1790             gRSP.clip_ratio_posy = -(short)w1;
1791             modified = true;
1792         }
1793         break;
1794     }
1795
1796     if( modified )
1797     {
1798         UpdateClipRectangle();
1799     }
1800
1801 }
1802 void CRender::UpdateClipRectangle()
1803 {
1804     if( status.bHandleN64RenderTexture )
1805     {
1806         //windowSetting.fMultX = windowSetting.fMultY = 1;
1807         windowSetting.vpLeftW = 0;
1808         windowSetting.vpTopW = 0;
1809         windowSetting.vpRightW = newRenderTextureInfo.bufferWidth;
1810         windowSetting.vpBottomW = newRenderTextureInfo.bufferHeight;
1811         windowSetting.vpWidthW = newRenderTextureInfo.bufferWidth;
1812         windowSetting.vpHeightW = newRenderTextureInfo.bufferHeight;
1813
1814         gRSP.vtxXMul = windowSetting.vpWidthW/2.0f;
1815         gRSP.vtxXAdd = gRSP.vtxXMul + windowSetting.vpLeftW;
1816         gRSP.vtxYMul = -windowSetting.vpHeightW/2.0f;
1817         gRSP.vtxYAdd = windowSetting.vpHeightW/2.0f + windowSetting.vpTopW+windowSetting.toolbarHeightToUse;
1818
1819         // Update clip rectangle by setting scissor
1820
1821         int halfx = newRenderTextureInfo.bufferWidth/2;
1822         int halfy = newRenderTextureInfo.bufferHeight/2;
1823         int centerx = halfx;
1824         int centery = halfy;
1825
1826         gRSP.clip_ratio_left = centerx - halfx * gRSP.clip_ratio_negx;
1827         gRSP.clip_ratio_top = centery - halfy * gRSP.clip_ratio_negy;
1828         gRSP.clip_ratio_right = centerx + halfx * gRSP.clip_ratio_posx;
1829         gRSP.clip_ratio_bottom = centery + halfy * gRSP.clip_ratio_posy;
1830     }
1831     else
1832     {
1833         windowSetting.vpLeftW = int(gRSP.nVPLeftN * windowSetting.fMultX);
1834         windowSetting.vpTopW = int(gRSP.nVPTopN  * windowSetting.fMultY);
1835         windowSetting.vpRightW = int(gRSP.nVPRightN* windowSetting.fMultX);
1836         windowSetting.vpBottomW = int(gRSP.nVPBottomN* windowSetting.fMultY);
1837         windowSetting.vpWidthW = int((gRSP.nVPRightN - gRSP.nVPLeftN + 1) * windowSetting.fMultX);
1838         windowSetting.vpHeightW = int((gRSP.nVPBottomN - gRSP.nVPTopN + 1) * windowSetting.fMultY);
1839
1840         gRSP.vtxXMul = windowSetting.vpWidthW/2.0f;
1841         gRSP.vtxXAdd = gRSP.vtxXMul + windowSetting.vpLeftW;
1842         gRSP.vtxYMul = -windowSetting.vpHeightW/2.0f;
1843         gRSP.vtxYAdd = windowSetting.vpHeightW/2.0f + windowSetting.vpTopW+windowSetting.toolbarHeightToUse;
1844
1845         // Update clip rectangle by setting scissor
1846
1847         int halfx = gRSP.nVPWidthN/2;
1848         int halfy = gRSP.nVPHeightN/2;
1849         int centerx = gRSP.nVPLeftN+halfx;
1850         int centery = gRSP.nVPTopN+halfy;
1851
1852         gRSP.clip_ratio_left = centerx - halfx * gRSP.clip_ratio_negx;
1853         gRSP.clip_ratio_top = centery - halfy * gRSP.clip_ratio_negy;
1854         gRSP.clip_ratio_right = centerx + halfx * gRSP.clip_ratio_posx;
1855         gRSP.clip_ratio_bottom = centery + halfy * gRSP.clip_ratio_posy;
1856     }
1857
1858     UpdateScissorWithClipRatio();
1859 }
1860
1861 void CRender::UpdateScissorWithClipRatio()
1862 {
1863     gRSP.real_clip_scissor_left = std::max(gRDP.scissor.left, gRSP.clip_ratio_left);
1864     gRSP.real_clip_scissor_top = std::max(gRDP.scissor.top, gRSP.clip_ratio_top);
1865     gRSP.real_clip_scissor_right = std::min(gRDP.scissor.right,gRSP.clip_ratio_right);
1866     gRSP.real_clip_scissor_bottom = std::min(gRDP.scissor.bottom, gRSP.clip_ratio_bottom);
1867
1868     gRSP.real_clip_scissor_left = std::max(gRSP.real_clip_scissor_left, 0);
1869     gRSP.real_clip_scissor_top = std::max(gRSP.real_clip_scissor_top, 0);
1870     gRSP.real_clip_scissor_right = std::min(gRSP.real_clip_scissor_right,windowSetting.uViWidth-1);
1871     gRSP.real_clip_scissor_bottom = std::min(gRSP.real_clip_scissor_bottom, windowSetting.uViHeight-1);
1872
1873     WindowSettingStruct &w = windowSetting;
1874     w.clipping.left = (uint32)(gRSP.real_clip_scissor_left*windowSetting.fMultX);
1875     w.clipping.top  = (uint32)(gRSP.real_clip_scissor_top*windowSetting.fMultY);
1876     w.clipping.bottom = (uint32)(gRSP.real_clip_scissor_bottom*windowSetting.fMultY);
1877     w.clipping.right = (uint32)(gRSP.real_clip_scissor_right*windowSetting.fMultX);
1878     if( w.clipping.left > 0 || w.clipping.top > 0 || w.clipping.right < (uint32)windowSetting.uDisplayWidth-1 ||
1879         w.clipping.bottom < (uint32)windowSetting.uDisplayHeight-1 )
1880     {
1881         w.clipping.needToClip = true;
1882     }
1883     else
1884     {
1885         w.clipping.needToClip = false;
1886     }
1887     w.clipping.width = (uint32)((gRSP.real_clip_scissor_right-gRSP.real_clip_scissor_left+1)*windowSetting.fMultX);
1888     w.clipping.height = (uint32)((gRSP.real_clip_scissor_bottom-gRSP.real_clip_scissor_top+1)*windowSetting.fMultY);
1889
1890     float halfx = gRSP.nVPWidthN/2.0f;
1891     float halfy = gRSP.nVPHeightN/2.0f;
1892     float centerx = gRSP.nVPLeftN+halfx;
1893     float centery = gRSP.nVPTopN+halfy;
1894
1895     gRSP.real_clip_ratio_negx = (gRSP.real_clip_scissor_left - centerx)/halfx;
1896     gRSP.real_clip_ratio_negy = (gRSP.real_clip_scissor_top - centery)/halfy;
1897     gRSP.real_clip_ratio_posx = (gRSP.real_clip_scissor_right - centerx)/halfx;
1898     gRSP.real_clip_ratio_posy = (gRSP.real_clip_scissor_bottom - centery)/halfy;
1899
1900     ApplyScissorWithClipRatio(true);
1901 }
1902
1903
1904 // Set other modes not covered by color combiner or alpha blender
1905 void CRender::InitOtherModes(void)
1906 {
1907     ApplyTextureFilter();
1908
1909     //
1910     // I can't think why the hand in mario's menu screen is rendered with an opaque rendermode,
1911     // and no alpha threshold. We set the alpha reference to 1 to ensure that the transparent pixels
1912     // don't get rendered. I hope this doesn't fuck anything else up.
1913     //
1914     if ( gRDP.otherMode.alpha_compare == 0 )
1915     {
1916         if ( gRDP.otherMode.cvg_x_alpha && (gRDP.otherMode.alpha_cvg_sel || gRDP.otherMode.aa_en ) )
1917         {
1918             ForceAlphaRef(128); // Strange, I have to use value=2 for pixel shader combiner for Nvidia FX5200
1919                                 // for other video cards, value=1 is good enough.
1920             SetAlphaTestEnable(TRUE);
1921         }
1922         else
1923         {
1924             SetAlphaTestEnable(FALSE);
1925         }
1926     }
1927     else if ( gRDP.otherMode.alpha_compare == 3 )
1928     {
1929         //RDP_ALPHA_COMPARE_DITHER
1930         SetAlphaTestEnable(FALSE);
1931     }
1932     else
1933     {
1934         if( (gRDP.otherMode.alpha_cvg_sel ) && !gRDP.otherMode.cvg_x_alpha )
1935         {
1936             // Use CVG for pixel alpha
1937             SetAlphaTestEnable(FALSE);
1938         }
1939         else
1940         {
1941             // RDP_ALPHA_COMPARE_THRESHOLD || RDP_ALPHA_COMPARE_DITHER
1942             if( m_dwAlpha==0 )
1943                 ForceAlphaRef(1);
1944             else
1945                 ForceAlphaRef(m_dwAlpha);
1946             SetAlphaTestEnable(TRUE);
1947         }
1948     }
1949
1950     if( options.enableHackForGames == HACK_FOR_SOUTH_PARK_RALLY && m_Mux == 0x00121824ff33ffffLL &&
1951         gRSP.bCullFront && gRDP.otherMode.aa_en && gRDP.otherMode.z_cmp && gRDP.otherMode.z_upd )
1952     {
1953         SetZCompare(FALSE);
1954     }
1955
1956
1957     if( gRDP.otherMode.cycle_type  >= CYCLE_TYPE_COPY )
1958     {
1959         // Disable zbuffer for COPY and FILL mode
1960         SetZCompare(FALSE);
1961     }
1962     else
1963     {
1964         SetZCompare(gRDP.otherMode.z_cmp);
1965         SetZUpdate(gRDP.otherMode.z_upd);
1966     }
1967
1968     /*
1969     if( options.enableHackForGames == HACK_FOR_SOUTH_PARK_RALLY && m_Mux == 0x00121824ff33ffff &&
1970         gRSP.bCullFront && gRDP.otherMode.z_cmp && gRDP.otherMode.z_upd )//&& gRDP.otherMode.aa_en )
1971     {
1972         SetZCompare(FALSE);
1973         SetZUpdate(FALSE);
1974     }
1975     */
1976 }
1977
1978
1979 void CRender::SetTextureFilter(uint32 dwFilter)
1980 {
1981     if( options.forceTextureFilter == FORCE_DEFAULT_FILTER )
1982     {
1983         switch(dwFilter)
1984         {
1985             case RDP_TFILTER_AVERAGE:   //?
1986             case RDP_TFILTER_BILERP:
1987                 m_dwMinFilter = m_dwMagFilter = FILTER_LINEAR;
1988                 break;
1989             default:
1990                 m_dwMinFilter = m_dwMagFilter = FILTER_POINT;
1991                 break;
1992         }
1993     }
1994     else
1995     {
1996         switch( options.forceTextureFilter )
1997         {
1998         case FORCE_POINT_FILTER:
1999             m_dwMinFilter = m_dwMagFilter = FILTER_POINT;
2000             break;
2001         case FORCE_LINEAR_FILTER:
2002             m_dwMinFilter = m_dwMagFilter = FILTER_LINEAR;
2003             break;
2004         }
2005     }
2006
2007     ApplyTextureFilter();
2008 }
2009
2010 bool SaveRGBBufferToFile(char *filename, unsigned char *buf, int width, int height, int pitch)
2011 {
2012     if( pitch == -1 )
2013         pitch = width*3;
2014
2015     if( strcasecmp(right(filename,3),"bmp") == 0 )
2016     {
2017         BITMAPFILEHEADER fileHeader;
2018         BITMAPINFOHEADER infoHeader;
2019
2020         infoHeader.biSize = sizeof( BITMAPINFOHEADER );
2021         infoHeader.biWidth = width;
2022         infoHeader.biHeight = height;
2023         infoHeader.biPlanes = 1;
2024         infoHeader.biBitCount = 24;
2025         infoHeader.biCompression = BI_RGB;
2026         infoHeader.biSizeImage = width * height * 3;
2027         infoHeader.biXPelsPerMeter = 0;
2028         infoHeader.biYPelsPerMeter = 0;
2029         infoHeader.biClrUsed = 0;
2030         infoHeader.biClrImportant = 0;
2031
2032         fileHeader.bfType = 19778;
2033         fileHeader.bfSize = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + infoHeader.biSizeImage;
2034         fileHeader.bfReserved1 = fileHeader.bfReserved2 = 0;
2035         fileHeader.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER );
2036
2037
2038         FILE *f;
2039         f = fopen(filename, "wb");
2040         if(f != NULL)
2041         {
2042             if (fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f) != 1 ||
2043                 fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f) != 1 ||
2044                 fwrite(buf, infoHeader.biSizeImage, 1, f) != 1)
2045                 printf("failed to write out texture data to image file '%s'", filename);
2046             
2047             fclose(f);
2048             return true;
2049         }
2050         else
2051         {
2052             // Do something
2053             TRACE1("Fail to create file %s", filename);
2054             return false;
2055         }   
2056     }
2057     else
2058     {
2059         if( strcasecmp(right(filename,4),".png") != 0 ) strcat(filename,".png");
2060
2061         struct BMGImageStruct img;
2062             memset(&img, 0, sizeof(BMGImageStruct));
2063         InitBMGImage(&img);
2064         img.bits = buf;
2065         img.bits_per_pixel = 24;
2066         img.height = height;
2067         img.width = width;
2068         img.scan_width = pitch;
2069         BMG_Error code = WritePNG(filename, img);
2070
2071         if( code == BMG_OK )
2072             return true;
2073         else
2074             return false;
2075     }
2076 }
2077
2078 bool SaveRGBABufferToPNGFile(char *filename, unsigned char *buf, int width, int height, int pitch)
2079 {
2080     if( pitch == -1 )
2081         pitch = width*4;
2082
2083     if( strcasecmp(right(filename,4),".png") != 0 ) strcat(filename,".png");
2084
2085     struct BMGImageStruct img;
2086         memset(&img, 0, sizeof(BMGImageStruct));
2087     InitBMGImage(&img);
2088     img.bits = buf;
2089     img.bits_per_pixel = 32;
2090     img.height = height;
2091     img.width = width;
2092     img.scan_width = pitch;
2093     BMG_Error code = WritePNG(filename, img);
2094
2095     if( code == BMG_OK )
2096         return true;
2097     else
2098         return false;
2099 }
2100