1 /*---------------------------------------------------------------------------*
2   Project: viewer for BMP file
3   File:    viewer.c
4 
5   Copyright 1998, 1999 Nintendo.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Log: viewer.c,v $
14   Revision 1.3  2006/03/06 09:59:00  kawaset
15   Eliminated warnings.
16 
17   Revision 1.2  02/20/2006 04:13:13  mitu
18   changed include path from dolphin/ to revolution/.
19 
20   Revision 1.1  12/16/2005 08:34:25  urata
21   Initial check-in.
22 
23 
24     15    01/11/08 17:02 Hashida
25     Added PAL support.
26 
27     14    5/10/01 3:18p Carl
28     Added button control feature, XFB load feature.
29 
30     13    6/07/00 5:39p Tian
31     Updated idle function API
32 
33     12    5/15/00 1:37p Hashida
34     Changed maximum vi height from 482 to 480 for NTSC and MPAL.
35 
36     11    3/01/00 5:29p Hashida
37     Modified so that it doesn't have to include vicommon.h.
38 
39     10    3/01/00 5:17p Hashida
40     Removed tga file view.
41 
42     9     3/01/00 1:14p Hashida
43     Renamed dirent->filename to name.
44 
45     8     2/28/00 7:26p Hashida
46     Added to set viHeight for assertion.
47 
48     7     2/04/00 11:43a Hashida
49     Changed to do the read and conversion as background.
50     Support for single field interlaced images.
51 
52     6     1/28/00 9:02p Hashida
53     Added VIFlush.
54     Added code to safely change modes.
55     Changed because VI APIs doesn't set registers immediately anymore.
56 
57     5     1/26/00 3:56p Hashida
58     Changed to use VIConfigure.
59 
60     4     1/21/00 2:15a Hashida
61     Make it work for non-interlace mode
62 
63     3     1/20/00 2:57p Hashida
64     Make it work for NTSC_NONINTERLACE
65 
66     2     1/17/00 10:18a Hashida
67     Removed a warning.
68 
69     1     1/15/00 3:04a Hashida
70     Initial revision
71 
72     1     1/13/00 12:17p Hashida
73     Initial revision
74 
75   $NoKeywords: $
76  *---------------------------------------------------------------------------*/
77 
78 /*
79  * Open files under /pictures and show it on screen
80  */
81 #include <revolution.h>
82 #include <string.h>
83 #include <ctype.h>
84 #include "bmp.h"
85 
86 // for X frame buffer
87 static u8*  xfb1;
88 static u8*  xfb2;
89 
90 u32         first=1; // used to bring up first image immediately
91 u32         mode=0;  // flip mode; 0 = timer, 1 = button control
92 
93 #if defined(NTSC) || defined(MPAL)
94 
95 #define XFB_WD          720
96 #define XFB_HT          480
97 
98 #define SCREEN_WD       720
99 #ifndef NON_INTERLACE
100 #define SCREEN_HT       480
101 #else   // #ifndef NON_INTERLACE
102 #define SCREEN_HT       240
103 #endif  // #ifndef NON_INTERLACE
104 
105 #else   // #if defined(NTSC) || defined(MPAL)
106 
107 #define XFB_WD          720
108 #define XFB_HT          574
109 
110 #define SCREEN_WD       720
111 #ifndef NON_INTERLACE
112 #define SCREEN_HT       574
113 #else   // #ifndef NON_INTERLACE
114 #define SCREEN_HT       287
115 #endif  // #ifndef NON_INTERLACE
116 
117 #endif  // #if defined(NTSC) || defined(MPAL)
118 
119 
120 #define IS_BMP(p)   (! mystrcmp(p, "bmp"))
121 #define IS_BMA(p)   (! mystrcmp(p, "bma"))
122 #define IS_XFB(p)   (! mystrcmp(p, "xfb"))
123 
124 static void MyOSInit( void );
125 static void printOneLevel(char* pathName);
126 
127 #define TYPE_INIT       0
128 #define TYPE_BMP        1
129 #define TYPE_BMA        2
130 #define TYPE_XFB        3
131 
132 u8   Stack[4096];
133 
134 typedef struct
135 {
136     // input
137     u8*         image;      // processed image
138     DVDDir*     dir;
139 
140     // output
141     u32         height;     // image height
142     u32         width;      // image width
143     u8*         image2;     // processed image2 (for INT-SF mode)
144     u32         imageType;
145     BOOL        end;
146 } idleParam_s;
147 
148 struct dirs_t
149 {
150     struct dirs_t*  next;       // must be first
151     DVDDirEntry     dirEntry;
152 };
153 
154 typedef struct dirs_t       dirs;
155 
156 
157 // case insensitive strcmp
mystrcmp(char * str1,char * str2)158 static int mystrcmp(char* str1, char* str2)
159 {
160     while(*str1)
161     {
162         if (tolower(*str1) != tolower(*str2))
163             return (tolower(*str1) - tolower(*str2));
164         str1++;
165         str2++;
166     }
167 
168     return 0;
169 }
170 
171 
readConvertBmp(char * fileName,u8 * dest,u32 * width,u32 * height)172 static void readConvertBmp(char* fileName, u8* dest, u32* width, u32* height)
173 {
174     DVDFileInfo     finfo;
175     u32             length;
176     u8*             buf;
177     bmpInfo_s       bInfo;
178 
179     if (FALSE == DVDOpen(fileName, &finfo))
180     {
181         OSReport("Can't open file %s\n", fileName);
182         return;
183     }
184 
185     length = DVDGetLength(&finfo);
186 
187     OSReport("F %9d %s\n", length, fileName);
188 
189     if( NULL == (buf = OSAlloc(OSRoundUp32B(length))) )
190     {
191         OSReport("Alloc failed. Exit\n");
192         return;
193     }
194 
195     if (OSRoundUp32B(length) !=
196         DVDRead(&finfo, buf, (s32)OSRoundUp32B(length), 0))
197     {
198         OSReport("Error occurred when reading %s\n", fileName);
199         OSHalt("");
200     }
201 
202     if (FALSE == openBmp(&bInfo, buf))
203     {
204         OSReport("Failed to analyze %s as a bmp file\n",
205                  fileName);
206         OSHalt("");
207     }
208 
209     OSReport("  bfOffBits: %d\n", bInfo.bfOffBits);
210     OSReport("  width: %d\n", bInfo.width);
211     OSReport("  height: %d\n", bInfo.height);
212     OSReport("  biBitCount: %d\n", bInfo.biBitCount);
213     OSReport("  biCompression: %d\n", bInfo.biCompression);
214     OSReport("  biSizeImage: %d\n", bInfo.biSizeImage);
215     OSReport("  paletteOff: %d\n", bInfo.paletteOff);
216 
217     if (FALSE == bmpToYCbCr(&bInfo, buf, dest))
218     {
219         OSReport("Failed to convert bmp to YCbCr\n");
220         OSHalt("");
221     }
222 
223     *width = (bInfo.width + 15) / 16 * 16;
224     *height = (bInfo.height + 1) / 2 * 2;
225 
226     DVDClose(&finfo);
227     OSFree(buf);
228 }
229 
230 
getRenderMode(GXRenderModeObj * rm,u16 dispPosX,u16 dispPosY,u16 dispSizeX,VITVMode tvMode,u16 xfbSizeX,u16 xfbSizeY,VIXFBMode xfbMode)231 static void getRenderMode(GXRenderModeObj* rm, u16 dispPosX, u16 dispPosY,
232                           u16 dispSizeX, VITVMode tvMode,
233                           u16 xfbSizeX, u16 xfbSizeY,
234                           VIXFBMode xfbMode)
235 {
236     rm->viTVmode = tvMode;
237     rm->fbWidth = xfbSizeX;
238     rm->xfbHeight = xfbSizeY;
239     rm->viXOrigin = dispPosX;
240     rm->viYOrigin = dispPosY;
241     rm->viWidth = dispSizeX;
242     rm->xFBmode = xfbMode;
243 
244     rm->viHeight = (u16)((xfbMode == VI_XFBMODE_DF)? xfbSizeY : (u16)(xfbSizeY*2));
245 }
246 
idle(void * param)247 static void idle(void* param)
248 {
249     DVDDirEntry     dirent;
250     idleParam_s*    ip;
251     u32             width;
252     u32             height;
253     char*       p;
254 
255     ip = (idleParam_s*)param;
256     ip->end = FALSE;
257     ip->imageType = 0;
258 
259     do
260     {
261         if (FALSE == DVDReadDir(ip->dir, &dirent))
262         {
263             ip->end = TRUE;
264             return;
265         }
266 
267         p = dirent.name;
268         while(*p++ != '.')
269             ;
270 
271         if (IS_BMP(p))
272             ip->imageType = TYPE_BMP;
273 #ifndef NON_INTERLACE
274         else if (IS_BMA(p))
275             ip->imageType = TYPE_BMA;
276 #endif
277         else if (IS_XFB(p))
278             ip->imageType = TYPE_XFB;
279 
280     } while ( (ip->imageType == 0) || (dirent.isDir) );
281 
282     if (ip->imageType == TYPE_BMP)
283     {
284         readConvertBmp(dirent.name, ip->image, &width, &height);
285     }
286     else if (ip->imageType == TYPE_BMA)
287     {
288         u32         firstLength;
289         u32         fnlength;
290         char*       fn;
291 
292         readConvertBmp(dirent.name, ip->image, &width, &height);
293 
294         firstLength = width * height * 2;
295 
296         // read "bmb"
297         fnlength = strlen(dirent.name) + 1;
298         if( NULL == (fn = OSAlloc(OSRoundUp32B(fnlength))) )
299         {
300             OSReport("Alloc failed. Exit\n");
301             return;
302         }
303 
304         strcpy(fn, dirent.name);
305         fn[fnlength - 2] = 'b';
306 
307         OSReport("fn is %s\n", fn);
308 
309         readConvertBmp(fn, ip->image + firstLength, &width, &height);
310 
311         ip->image2 = ip->image + firstLength;
312     }
313     else if (ip->imageType == TYPE_XFB)
314     {
315         DVDFileInfo     finfo;
316         u32 length;
317 
318         // read file into ip->image
319 
320         if (FALSE == DVDOpen(dirent.name, &finfo))
321         {
322             OSReport("Can't open file %s\n", dirent.name);
323             return;
324         }
325 
326         length = DVDGetLength(&finfo);
327 
328         OSReport("F %9d %s\n", length, dirent.name);
329 
330         width = 640;
331         height = length / (640*2);
332 
333         if (OSRoundUp32B(length) !=
334             DVDRead(&finfo, ip->image, (s32)OSRoundUp32B(length), 0))
335         {
336             OSReport("Error occurred when reading %s\n", dirent.name);
337             OSHalt("");
338         }
339         DVDClose(&finfo);
340     }
341 
342     if (height > SCREEN_HT)
343         height = SCREEN_HT;
344 
345     if (ip->imageType != TYPE_BMA)
346         DCStoreRange((void*)ip->image, width * height * 2);
347     else    // store caches for two images
348         DCStoreRange((void*)ip->image, width * height * 2 * 2);
349 
350     ip->height = height;
351     ip->width = width;
352 }
353 
printOneLevel(char * pathName)354 static void printOneLevel(char* pathName)
355 {
356     DVDDir          dir;
357     dirs*           start = (dirs*)NULL;
358     dirs*           curr;
359     char            path[256];
360     static u32      imageReady = 0;
361     static u8*      xfb;
362     static u32      frame = 0;
363     static u32      prevCount;
364     static u32      width;
365     static u32      height;
366     static u32      isBma;
367     GXRenderModeObj rm;
368     OSThread*       background;
369     idleParam_s     ip;
370     u32             startProcessNext;
371 
372     PADStatus       pstat[PAD_MAX_CONTROLLERS];
373     u32             plast=0;
374 
375 #pragma unused(pathName)
376 
377     curr = (dirs*)&start;
378 
379     if (FALSE == DVDOpenDir(".", &dir))
380     {
381         OSReport("Can't open dir %s\n", path);
382         return;
383     }
384 
385     xfb = (frame)? xfb2 : xfb1;
386 
387     startProcessNext = 1;
388     while(1)
389     {
390         VIWaitForRetrace();
391 
392         if (startProcessNext)
393         {
394             ip.image = (xfb == xfb1)? xfb2 : xfb1;
395             ip.dir = &dir;
396             background = OSSetIdleFunction(idle, (void*)&ip,
397                                            Stack + sizeof Stack,
398                                            sizeof Stack);
399             startProcessNext = 0;
400         }
401 
402         PADRead(pstat);
403 
404         if ((pstat[0].button & PAD_BUTTON_B) &&
405             !(plast & PAD_BUTTON_B))
406         {
407             mode = 1 - mode;
408             if (mode)
409                 OSReport("Button A control mode\n");
410             else
411                 OSReport("Timer control mode\n");
412         }
413 
414         if (OSGetIdleFunction() == 0)
415         {
416             if (ip.end)
417                 return;
418 
419             if ( (mode && (pstat[0].button & PAD_BUTTON_A) &&
420                   !(plast & PAD_BUTTON_A)) || first ||
421                  (!mode && (VIGetRetraceCount() - prevCount >= 300)) )
422             {
423                 first = 0;
424 
425                 prevCount = VIGetRetraceCount();
426 
427                 frame ^= 1;
428                 xfb = (frame)? xfb2 : xfb1;
429 
430                 width = ip.width;
431                 height = ip.height;
432                 isBma = (ip.imageType == TYPE_BMA);
433 
434 #ifndef NON_INTERLACE
435                 if (isBma)
436                     getRenderMode(&rm, (u16)((SCREEN_WD - width) / 2),
437                                   (u16)((SCREEN_HT - height * 2) / 2),
438                                   (u16)width,
439 #ifdef PAL
440                                   VI_TVMODE_PAL_INT,
441 #else
442                                   VI_TVMODE_NTSC_INT,
443 #endif
444                                   (u16)width, (u16)height,
445                                   VI_XFBMODE_SF);
446                 else
447                     getRenderMode(&rm, (u16)((SCREEN_WD - width) / 2),
448                                   (u16)((SCREEN_HT - height) / 2),
449                                   (u16)width,
450 #ifdef PAL
451                                   VI_TVMODE_PAL_INT,
452 #else
453                                   VI_TVMODE_NTSC_INT,
454 #endif
455                                   (u16)width, (u16)height,
456                                   VI_XFBMODE_DF);
457 #else
458                 getRenderMode(&rm, (u16)((SCREEN_WD - width) / 2),
459                               (u16)((SCREEN_HT - height) / 2),
460                               (u16)width,
461 #ifdef PAL
462                               VI_TVMODE_PAL_DS,
463 #else
464                               VI_TVMODE_NTSC_DS,
465 #endif
466                               (u16)width, (u16)height,
467                               VI_XFBMODE_SF);
468 #endif
469 
470                 VIConfigure(&rm);
471 
472                 startProcessNext = 1;
473                 imageReady = 1;     // holds 1 after the first image gets ready
474             }
475 
476         }
477 
478         plast = pstat[0].button;
479 
480         if (isBma)
481         {
482             u8*     xfbSF;
483 
484             xfbSF = xfb;
485 
486             if (VIGetNextField() == VI_FIELD_ABOVE)
487                 xfbSF = xfb;
488             else
489                 xfbSF = xfb + width * height * 2;
490 
491             VISetNextFrameBuffer(xfbSF);
492         }
493         else
494             VISetNextFrameBuffer(xfb);
495 
496         if (imageReady)
497         {
498             VISetBlack(FALSE);
499         }
500 
501         VIFlush();
502 
503     } // while(1)
504 
505 } // void printOneLevel(char*)
506 
507 
main(void)508 void main(void)
509 {
510     MyOSInit();
511 
512     PADInit();
513 
514     OSReport("Image File Viewer\n\n");
515     OSReport("Press Button B to change flip mode\n\n");
516 
517     if (FALSE == DVDChangeDir("pictures"))
518     {
519         OSReport("Can't change dir to pictures\n");
520         return;
521     }
522 
523     while(1)
524         printOneLevel(".");
525 
526     OSHalt("End of program");
527 
528     // NOT REACHED HERE
529 }
530 
531 
532  /*---------------------------------------------------------------------------*
533     Name:               MyOSInit
534 
535     Description:        Initialize the operating system.
536                         Create a heap so we can use OSAlloc().
537 
538     Arguments:          none
539 
540     Returns:            none
541  *---------------------------------------------------------------------------*/
MyOSInit(void)542 static void MyOSInit ( void )
543 {
544     void*               arenaLo;
545     void*               arenaHi;
546 
547     OSInit();
548     DVDInit();
549     VIInit();
550 
551     arenaLo = OSGetArenaLo();
552     arenaHi = OSGetArenaHi();
553 
554     // allocate memory for frame buffer here.
555     xfb1 = (u8*)OSRoundUp32B(arenaLo);
556     xfb2 = (u8*)OSRoundUp32B(arenaLo)
557                 + XFB_HT * XFB_WD * VI_DISPLAY_PIX_SZ;
558     arenaLo = (void*)(xfb1 + XFB_HT * XFB_WD * VI_DISPLAY_PIX_SZ * 2);
559     OSSetArenaLo(arenaLo);
560 
561     // OSInitAlloc should only ever be invoked once.
562     arenaLo = OSInitAlloc(arenaLo, arenaHi, 1); // 1 heap
563     OSSetArenaLo(arenaLo);
564 
565     // The boundaries given to OSCreateHeap should be 32B aligned
566     OSSetCurrentHeap(OSCreateHeap((void*)OSRoundUp32B(arenaLo),
567                                   (void*)OSRoundDown32B(arenaHi)));
568     // From here on out, OSAlloc and OSFree behave like malloc and free
569     // respectively
570 
571     OSSetArenaLo(arenaLo = arenaHi);
572 
573     return;
574 }
575