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