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