1 /*---------------------------------------------------------------------------*
2   Project:  CARD icon viewer
3   File:     listdemo.c
4 
5   Copyright 2000-2001 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: listdemo.c,v $
14   Revision 1.2  02/20/2006 04:13:08  mitu
15   changed include path from dolphin/ to revolution/.
16 
17   Revision 1.1  01/31/2006 10:48:30  mitu
18   (none)
19 
20 
21     23    11/21/01 21:41 Shiki
22     Removed obsolete OS function calls
23 
24     22    8/23/01 14:21 Shiki
25     Modified to use OSResetSystem for restart.
26 
27     21     7/13/01 12:01:00 Shiki
28     Implemented more friendly free space check routines.
29 
30     20     7/12/01 21:24:00 Shiki
31     Added support for Japanese menu.
32 
33     19    01/06/25 14:38 Shiki
34     Modified ProbeAnimTick() using CARDProbeEx().
35 
36     18    01/06/22 11:05 Shiki
37     Removed redundant VIWaitForRetrace().
38 
39     17    01/06/19 13:49 Shiki
40     Minor fix.
41 
42     16    5/18/01 5:30p Shiki
43     Modified to show progress bar.
44 
45     15    5/17/01 8:22p Shiki
46     Revised using new OSResetSystem function.
47 
48     14    5/11/01 5:05p Shiki
49     Revised using new DEMO API interface.
50 
51     13    01/04/26 10:38 Shiki
52     Set banner background color to black.
53 
54     12    01/04/25 14:57 Shiki
55     Merged US console font size setting.
56 
57     11    01/04/25 14:35 Shiki
58     Revised using new DEMO API.
59 
60     10    01/04/25 11:40 Shiki
61     Modified to load disk banner file.
62 
63     9     01/04/23 17:26 Shiki
64     Revised using cardutil.c
65 
66     8     01/04/10 16:50 Shiki
67     Added support for icon animation speed.
68 
69     7     01/03/06 17:19:00 Shiki
70     Modified InitCont() to support controller recalibration.
71 
72     6     01/02/22 13:10 Shiki
73     Added support for multiple sector sizes.
74 
75     5     01/01/23 9:35 Shiki
76     Fixed typo.
77 
78     4     12/12/00 12:39a Shiki
79     Bug fix.
80 
81     3     12/11/00 11:13p Shiki
82     Revised to display icons.
83 
84     3     8/10/00 4:39p Shiki
85     Modified CARDStat.length from u8 to u32.
86 
87     2     8/08/00 5:23p Shiki
88     Improved the output format.
89 
90     1     7/14/00 4:16p Shiki
91     Initial check-in.
92   $NoKeywords: $
93  *---------------------------------------------------------------------------*/
94 
95 #include <stdarg.h>
96 #include <string.h>
97 #include <demo.h>
98 #include <revolution.h>
99 #include <revolution/dvd/DVDBanner.h>
100 #include <revolution/os/OSReset.h>
101 #include "cont.h"
102 #include "cardutil.h"
103 
104 GXColor Black = {   0,   0,   0, 0 };
105 GXColor Blue  = {   0,   0, 192, 0 };
106 GXColor Red   = { 255,   0,   0, 0 };
107 GXColor Green = {   0, 224,   0, 0 };
108 GXColor White = { 255, 255, 255, 0 };
109 
110 enum
111 {
112     STR_Null,
113     STR_Cancel,
114     STR_Select,
115     STR_Confirm,
116     STR_Finish,
117     STR_Yes,
118     STR_No,
119     STR_Save,
120     STR_Erase,
121 
122     STR_Ignore,
123     STR_Menu,
124 
125     STR_Mounting,
126 
127     STR_Saving,
128     STR_Saved,
129     STR_NotSaved,
130     STR_MayNotSaved,
131 
132     STR_Erasing,
133     STR_Erased,
134     STR_MayNotErased,
135 
136     STR_Formatting,
137     STR_Formatted,
138     STR_MayNotFormatted,
139 
140     STR_Format,
141 
142     STR_InsSpace,
143 
144     STR_MemoryCard,
145     STR_NotMemoryCard,
146     STR_Nothing,
147     STR_Broken,
148     STR_Open,
149     STR_Stat,
150     STR_End
151 };
152 
153 char* StringResource[2][STR_End] =
154 {
155     // US English version
156     {
157         NULL,
158         "Cancel",
159         "Select",
160         "Confirm",
161         "Finish",
162         "Yes",
163         "No",
164         "Save",
165         "Erase",
166 
167         "Continue",
168         "Go to GameCube main menu",
169 
170         "Loading...",
171 
172         "Saving...",
173         "The data was saved.",
174         "The data has not been saved.",
175         "The data may not have been saved.",
176 
177         "Erasing...",
178         "The data was erased.",
179         "The data may not have been erased.",
180 
181         "Formatting...",
182         "The Memory Card was formatted.",
183         "The Memory Card may not have been formatted.",
184 
185         "The Memory Card must be formatted. \n"
186         "If the Memory Card is formatted, all saved \n"
187         "data will be erased. \n"
188         "Format it now?",
189 
190         "The Memory Card has no space available or \n"
191         "it has too many files saved to it to play \n"
192         "the game. Game data cannot be saved.",
193 
194         "Inserted (%dMbits, %dK sector)",
195         "Inserted (not memory card)",
196         "Nothing is inserted",
197         "The object inserted cannot be used.",
198         "<Open>",
199         "%d blocks free. %d files free.",
200     },
201 
202     // Japanese version
203     {
204         NULL,
205         "�߂�",
206         "�I��",
207         "����",
208         "�I��",
209         "�͂�",
210         "������",
211         "�ۑ�",
212         "����",
213 
214         "�ۑ����Ȃ��Ői�߂�",
215         "�Q�[���L���[�u ���C�����j���[��",
216 
217         "�ǂݍ��ݒ��ł��B",
218 
219         "�ۑ����ł��B",
220         "�ۑ����܂����B",
221         "�ۑ��ł��܂���ł����B",
222         "�ۑ��Ɏ��s�����”\��������܂��B",
223 
224         "�������ł��B",
225         "�������܂����B",
226         "�����Ɏ��s�����”\��������܂��B",
227 
228         "���������ł��B",
229         "���������܂����B",
230         "�������Ɏ��s�����”\��������܂��B",
231 
232         "�������[�J�[�h�͏��������K�v�ł��B\n"
233         "����������ƃ������[�J�[�h���̑S�t�@�C����\n"
234         "�����܂��B\n"
235         "���������܂����H",
236 
237         "�������[�J�[�h�ɃQ�[���ɕK�v�ȋ��e�ʂ��Ȃ����A\n"
238         "�t�@�C�����̐��������Ă��܂��܂��B\n"
239         "���̂܂܎n�߂�ƁA�ۑ����邱�Ƃ��ł��܂���B",
240 
241         "�������[�J�[�h (%dMbits, %dKB �Z�N�^�[)",
242         "�X���b�g�ɂ������Ă�����͎̂g���܂���B",
243         "�����������Ă��܂���B",
244         "�X���b�g�ɂ������Ă�����͎̂g���܂���B",
245         "<��>",
246         "���e�� %d �u���b�N�B���t�@�C���� %d�B",
247     },
248 };
249 
250 #define GetString(str)  (StringResource[OSGetFontEncode()][str])
251 
252 //
253 // Define save data size of the game in bytes
254 //
255 #define FILE_SIZE   (32 * 1024)
256 
257 typedef struct Interface
258 {
259     void (*DrawTick)(void);
260     void (*AnimTick)(void);
261 } Interface;
262 
263 enum
264 {
265     STATE_PROBE = 0,                    // State
266     STATE_LIST,
267 
268     SUBSTATE_SELECT = 0,                // SubState
269     SUBSTATE_COMMAND,
270     SUBSTATE_VERIFY,
271     SUBSTATE_BUSY,
272     SUBSTATE_WAIT,
273 
274     YES = 0,
275     NO,
276 
277     CARDUTIL_CMD_CHECKSPACE = 0x1000,
278     CARDUTIL_CMD_IGNORE,
279     CARDUTIL_CMD_MAIN_MENU,
280     CARDUTIL_CMD_ERROR
281 };
282 
283 #define LIST_ROW    8                   // # of rows in the list screen
284 
285 // Work area for card threads and card API
286 static u8             CardStack[8192]                  ATTRIBUTE_ALIGN(32);
287 static u8             CardWorkArea[CARD_WORKAREA_SIZE] ATTRIBUTE_ALIGN(32);
288 
289 // Save data template
290 static DVDBanner*     Banner;
291 static CARDStat       CardStatTemplate; // CARDStat for save file
292 static void*          CardData;         // Pointer to file save data
293 static TPLPalettePtr  TplBanner;        // Card banner texture image
294 static TPLPalettePtr  TplIcons;         // Card icon texture image
295 
296 // Program state
297 static int    Slot     = 0;             // Current memory card slot
298 static int    State    = STATE_PROBE;   // Current program state
299 static int    SubState;                 // Current program substate
300 static s32    Command;                  // Current command
301 static OSTime BeginWaitAt;              // Start time of the SUBSTATE_WAIT substate
302 static int    YesNo;                    // 0: Yes, No: 1
303 
304 // Directory
305 static CardUtilDirent Directory[CARD_MAX_FILE]         ATTRIBUTE_ALIGN(32);
306 static BOOL   List;                     // TRUE if Directory is valid
307 static int    Offset;                   // The first line number in the directory on screen
308 static int    Current;                  // The currently selected entry number
309 static s32    NumEntries;               // # of entries in Directory
310 
311 static void ProbeDrawTick(void);
312 static void ProbeAnimTick(void);
313 static void ListDrawTick(void);
314 static void ListAnimTick(void);
315 
316 static Interface InterfaceTable[] =
317 {
318     ProbeDrawTick,  ProbeAnimTick,
319     ListDrawTick,   ListAnimTick,
320 };
321 
322 static BOOL   ForceMenu;                // TRUE if goes back to GameCube IPL main menu
323 
324 OSFontHeader* FontData;                 // Pointer to ROM font data
325 
326 /*---------------------------------------------------------------------------*
327   Name:         DrawRectangle
328 
329   Description:  Draws rectangle
330  *---------------------------------------------------------------------------*/
DrawRectangle(int x,int y,int cx,int cy,GXColor color)331 static void DrawRectangle(int x, int y, int cx, int cy, GXColor color)
332 {
333     // Set rendering mode
334     GXSetNumChans(1);           // # of color channels
335     GXSetChanCtrl(GX_COLOR0,
336                GX_FALSE,        // passed through
337                GX_SRC_REG,      // amb source
338                GX_SRC_REG,      // mat source
339                GX_LIGHT_NULL,   // light mask
340                GX_DF_NONE,      // diffuse function
341                GX_AF_NONE );    // atten   function
342     GXSetChanMatColor(GX_COLOR0, color);
343 
344     GXSetNumTexGens(0);         // # of Tex gens
345     GXSetNumTevStages(1);       // # of Tev Stage
346     GXSetTevOrder(GX_TEVSTAGE0,
347                   GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
348     GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
349 
350     // Draw rectangle
351     GXClearVtxDesc();
352     GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
353     GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0);
354     GXBegin(GX_QUADS, GX_VTXFMT0, 4);
355         GXPosition3s16((s16) (x),      (s16) (y),      0);
356         GXPosition3s16((s16) (x + cx), (s16) (y),      0);
357         GXPosition3s16((s16) (x + cx), (s16) (y + cy), 0);
358         GXPosition3s16((s16) (x),      (s16) (y + cy), 0);
359     GXEnd( );
360 
361     // Restore rendering mode as same as DEMOInitCaption()
362     GXSetNumChans(0);
363     GXSetNumTexGens(0);         // # of Tex gens
364     GXSetNumTevStages(1);
365     GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
366     GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
367 }
368 
369 /*---------------------------------------------------------------------------*
370   Name:         DrawMenu
371 
372   Description:  ex. DrawMenu(x, y, color, current, item0, item1, NULL);
373 
374                 item must be a character string
375  *---------------------------------------------------------------------------*/
DrawMenu(int x,int y,GXColor color,int current,...)376 static int DrawMenu(int x, int y, GXColor color, int current, ...)
377 {
378     va_list ap;
379     char*   item;
380     int     cx;
381     s32     width;
382     int     i;
383     s16     size;
384     s16     space;
385 
386     cx = 0;
387     va_start(ap, current);
388     while ((item = va_arg(ap, char*)) != NULL)
389     {
390         width = DEMOGetRFTextWidth(item);
391         if (cx < width)
392         {
393             cx = width;
394         }
395     }
396     va_end(ap);
397 
398     i = 0;
399     DEMOGetROMFontSize(&size, &space);
400     y += FontData->leading * size / FontData->cellWidth;
401     va_start(ap, current);
402     while ((item = va_arg(ap, char*)) != NULL)
403     {
404         DEMOSetFontType(DM_FT_OPQ);
405         DrawRectangle(x, y - FontData->ascent * size / FontData->cellWidth, cx, FontData->leading * size / FontData->cellWidth,
406                       (i++ == current) ? color : Black);
407         DEMOSetFontType(DM_FT_XLU);
408         DEMORFPrintf((s16) x, (s16) y, 0, item);
409         y += DEMOGetRFTextHeight(item);
410     }
411     va_end(ap);
412 
413     return cx;
414 }
415 
416 /*---------------------------------------------------------------------------*
417   Name:         DrawMessage
418 
419   Description:  Draws message
420  *---------------------------------------------------------------------------*/
DrawMessage(int x,int y,GXColor color,char * message)421 static int DrawMessage(int x, int y, GXColor color, char* message)
422 {
423     int cy;
424     int cx;
425     s16 size;
426     s16 space;
427 
428     cx = DEMOGetRFTextWidth(message);
429     cy = DEMOGetRFTextHeight(message);
430 
431     DEMOSetFontType(DM_FT_OPQ);
432     DrawRectangle(x, y, cx, cy, color);
433 
434     DEMOGetROMFontSize(&size, &space);
435     y += FontData->leading * size / FontData->cellWidth;
436     DEMOSetFontType(DM_FT_XLU);
437     DEMORFPrintf((s16) x, (s16) y, 0, message);
438 
439     return cx;
440 }
441 
442 /*---------------------------------------------------------------------------*
443   Name:         DrawUsage
444 
445   Description:  Draws usages of buttons
446  *---------------------------------------------------------------------------*/
DrawUsage(int slot,char * commandA,char * commandB,char * message)447 static int DrawUsage(int slot, char* commandA, char* commandB, char* message)
448 {
449     int width;
450 
451     width = DEMORFPrintf(16, 45, 0, "SLOT-%c: ", "AB"[slot]);
452     if (commandB)
453     {
454         width += DEMORFPrintf((s16) (16 + width), 45, 0, "<B> %s ", commandB);
455     }
456     if (commandA)
457     {
458         width += DEMORFPrintf((s16) (16 + width), 45, 0, "<A> %s ", commandA);
459     }
460     if (message)
461     {
462         width += DEMORFPrintf((s16) (16 + width), 45, 0, message);
463     }
464     return width;
465 }
466 
467 /*---------------------------------------------------------------------------*
468   Name:         GetBlockSize
469 
470   Description:  Returns the number of required blocks to save the specified
471                 size of data in the current memory card.
472  *---------------------------------------------------------------------------*/
GetBlockSize(s32 size)473 static s32 GetBlockSize(s32 size)
474 {
475     return (size + CardUtilSectorSize() - 1) / CardUtilSectorSize();
476 }
477 
478 /*---------------------------------------------------------------------------*
479   Name:         IsSpaceAvailable
480 
481   Description:  Checks if a file of the specified size can be created in
482                 the current memory card.
483 
484   Note:         This function is for reliable file save. It requires the
485                 same number of temporary free blocks as the number of saved
486                 file will consume as well as one free file directory entry.
487 
488   Arguments:    size        file size in bytes
489 
490   Returns:      TRUE is space is available. Otherwise FALSE.
491  *---------------------------------------------------------------------------*/
IsSpaceAvailable(s32 size)492 static BOOL IsSpaceAvailable(s32 size)
493 {
494     s32 numFiles;
495     s32 numBlocks;
496 
497     numFiles = CardUtilNumFiles();
498     numBlocks = GetBlockSize(size);
499     if (CardUtilFilesNotUsed() == 0 ||
500         numFiles == 0 && CardUtilBlocksNotUsed() < 2 * numBlocks ||
501         0 < numFiles && CardUtilBlocksNotUsed() < numBlocks)
502     {
503         return FALSE;
504     }
505     else
506     {
507         return TRUE;
508     }
509 }
510 
511 /*---------------------------------------------------------------------------*
512   Name:         ProbeDrawTick
513 
514   Description:  Draw memory card slot selection screen
515  *---------------------------------------------------------------------------*/
ProbeDrawTick(void)516 static void ProbeDrawTick(void)
517 {
518     GXRenderModeObj* rmp;
519     s32              chan;
520 
521     rmp = DEMOGetRenderModeObj();
522     DEMOInitCaption(DM_FT_XLU, (s16) rmp->fbWidth, (s16) rmp->efbHeight);
523 
524     DEMOSetFontType(DM_FT_RVS);
525     DrawUsage(Slot, GetString(STR_Select), GetString(STR_Null), NULL);
526 
527     for (chan = 0; chan < 2; ++chan)
528     {
529         s32 memSize;
530         s32 sectorSize;
531         int width;
532 
533         DEMOSetFontType((Slot == chan) ? DM_FT_RVS : DM_FT_XLU);
534         width = DEMORFPrintf(16, (s16) ((chan == 0) ? 75 : 105), 0, " SLOT-%c: ", "AB"[chan]);
535         switch (CARDProbeEx(chan, &memSize, &sectorSize))
536         {
537           case CARD_RESULT_READY:
538             DEMORFPrintf((s16) (16 + width), (s16) ((chan == 0) ? 75 : 105), 0, GetString(STR_MemoryCard),
539                          memSize, sectorSize / 1024);
540             break;
541           case CARD_RESULT_WRONGDEVICE:
542             DEMORFPrintf((s16) (16 + width), (s16) ((chan == 0) ? 75 : 105), 0, GetString(STR_NotMemoryCard));
543             break;
544           default:
545             DEMORFPrintf((s16) (16 + width), (s16) ((chan == 0) ? 75 : 105), 0, GetString(STR_Nothing));
546             break;
547         }
548     }
549 
550     //
551     // Draw DVD banner data
552     //
553     // Note: In GameCube file viewer, not all the characters appears on screen.
554     //       Effective areas are shown as green boxes.
555     //
556 
557     DEMOSetFontType(DM_FT_OPQ);
558     DrawRectangle(16, 330, 2 * DVD_BANNER_WIDTH, 2 * DVD_BANNER_HEIGHT, Black);
559     CardUtilDrawIcon(16, 330, 2 * DVD_BANNER_WIDTH,
560                      Banner->image,
561                      NULL,
562                      DVD_BANNER_WIDTH, DVD_BANNER_HEIGHT,
563                      CARD_STAT_ICON_RGB5A3);
564 
565     DEMOSetFontType(DM_FT_XLU);
566 
567     // 'titl', gam_lay 18h, -1, 1ca0h
568     DEMOSetFontType(DM_FT_OPQ);
569     DrawRectangle(16, 200 - FontData->ascent, 458, FontData->leading, Green);
570     DEMOSetFontType(DM_FT_XLU);
571     DEMOSetROMFontSize(24, -1);
572     DEMORFPrintf(16, 200, 0, "%-64.64s",    Banner->longTitle);
573 
574     // 'makr', gam_lay 14h, -1, 1ca0h
575     DEMOSetFontType(DM_FT_OPQ);
576     DrawRectangle(16, 224 - FontData->ascent, 458, FontData->leading, Green);
577     DEMOSetFontType(DM_FT_XLU);
578     DEMOSetROMFontSize(20, -1);
579     DEMORFPrintf(16, 224, 0, "%-64.64s",    Banner->longMaker);
580 
581     // 'info', gam_lay 14h, -1, 1ca0h * 2
582     DEMOSetFontType(DM_FT_OPQ);
583     DrawRectangle(16, 250 - FontData->ascent, 458, 2 * FontData->leading + 15, Green);
584     DEMOSetFontType(DM_FT_XLU);
585     DEMOSetROMFontSize(20, -1);
586     DEMORFPutsEx(16, 265, 0, (char*) Banner->comment, 458, sizeof Banner->comment);
587 
588     // 'game', lay_gam 12h, -1, 0c00h (jp)
589     // 'game', lay_gam 10h, -2, 0c00h (us)
590     DEMOSetFontType(DM_FT_OPQ);
591     DrawRectangle(220, 354 - FontData->ascent, 192, FontData->leading, Green);
592     DEMOSetFontType(DM_FT_XLU);
593     if (OSGetFontEncode() == OS_FONT_ENCODE_SJIS)
594     {
595         DEMOSetROMFontSize(18, -1);
596     }
597     else
598     {
599         DEMOSetROMFontSize(16, -2);
600     }
601     DEMORFPrintf(220, 354, 0, "%-32.32s",   Banner->shortTitle);
602 
603     // 'makr', lay_gam 12h, -1, 0c00h (jp)
604     // 'makr', lay_gam 10h, -2, 0c00h (us)
605     DEMOSetFontType(DM_FT_OPQ);
606     DrawRectangle(220, 378 - FontData->ascent, 192, FontData->leading, Green);
607     DEMOSetFontType(DM_FT_XLU);
608     if (OSGetFontEncode() == OS_FONT_ENCODE_SJIS)
609     {
610         DEMOSetROMFontSize(18, -1);
611     }
612     else
613     {
614         DEMOSetROMFontSize(16, -2);
615     }
616     DEMORFPrintf(220, 378, 0, "%-32.32s",   Banner->shortMaker);
617 
618     DEMOSetROMFontSize((s16) FontData->cellWidth, -1);
619 }
620 
621 /*---------------------------------------------------------------------------*
622   Name:         ProbeAnimTick
623 
624   Description:  Switches the current slot selection.
625                 Starts the mount operation if A button is pushed.
626  *---------------------------------------------------------------------------*/
ProbeAnimTick(void)627 static void ProbeAnimTick(void)
628 {
629     s32 memSize;
630     s32 sectorSize;
631 
632     if (Conts[4].repeat & PAD_BUTTON_UP)
633     {
634         Slot = 0;
635     }
636     else if (Conts[4].repeat & PAD_BUTTON_DOWN)
637     {
638         Slot = 1;
639     }
640     else if ((Conts[4].down & PAD_BUTTON_A) &&
641              CARDProbeEx(Slot, &memSize, &sectorSize) == CARD_RESULT_READY)
642     {
643         CardUtilMount(Slot, CardWorkArea);
644         List     = FALSE;
645         Offset   = 0;
646         Current  = 0;
647         State    = STATE_LIST;
648         SubState = SUBSTATE_SELECT;
649 
650         // At first, the program should check the available space in the
651         // memory card.
652         Command  = CARDUTIL_CMD_CHECKSPACE;
653     }
654 }
655 
656 /*---------------------------------------------------------------------------*
657   Name:         DrawProgressBar
658 
659   Description:  Draw progress bar
660  *---------------------------------------------------------------------------*/
DrawProgressBar(void)661 static void DrawProgressBar(void)
662 {
663     DEMOSetFontType(DM_FT_OPQ);
664     DrawRectangle(16, 250, 300,                           25, Black);
665     DrawRectangle(16, 250, 3 * CardUtilGetProgress(Slot), 25, Green);
666 }
667 
668 /*---------------------------------------------------------------------------*
669   Name:         DrawInstructions
670 
671   Description:  Draw command instructions on screen.
672  *---------------------------------------------------------------------------*/
DrawInstructions(void)673 static void DrawInstructions(void)
674 {
675     s32 result;
676     int width;
677 
678     DEMOSetROMFontSize((s16) FontData->cellWidth, -1);
679     result = CardUtilResultCode();
680     switch (Command)
681     {
682       case CARDUTIL_CMD_SAVE:
683         DEMOSetFontType(DM_FT_RVS);
684         switch (SubState)
685         {
686           case SUBSTATE_COMMAND:
687             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Cancel), NULL);
688             DrawMenu(200, 200, Green, 0, GetString(STR_Save), GetString(STR_Erase), NULL);
689             break;
690           case SUBSTATE_VERIFY:
691             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Cancel), NULL);
692             width = DrawMenu(200, 200, Green, 0, GetString(STR_Save), GetString(STR_Erase), NULL);
693             DrawMenu(200 + 5 + width, 200, Green,
694                      YesNo, GetString(STR_Yes), GetString(STR_No), NULL);
695             break;
696           case SUBSTATE_BUSY:
697             DrawProgressBar();
698             DEMOSetFontType(DM_FT_RVS);
699             DrawUsage(Slot, GetString(STR_Null), GetString(STR_Null), GetString(STR_Saving));
700             break;
701           case SUBSTATE_WAIT:
702             DrawProgressBar();
703             DEMOSetFontType(DM_FT_RVS);
704             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Null), NULL);
705             switch (result)
706             {
707               case CARD_RESULT_READY:
708                 DrawMessage(16, 200, Green, GetString(STR_Saved));
709                 break;
710               case CARD_RESULT_NOENT:
711               case CARD_RESULT_INSSPACE:
712                 DrawMessage(16, 200, Red,   GetString(STR_NotSaved));
713                 break;
714               default:
715                 DrawMessage(16, 200, Red,   GetString(STR_MayNotSaved));
716                 break;
717             }
718             break;
719         }
720         break;
721 
722       case CARDUTIL_CMD_ERASE:
723         DEMOSetFontType(DM_FT_RVS);
724         switch (SubState)
725         {
726           case SUBSTATE_COMMAND:
727             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Cancel), NULL);
728             DrawMenu(200, 200, Green, 1, GetString(STR_Save), GetString(STR_Erase), NULL);
729             break;
730           case SUBSTATE_VERIFY:
731             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Cancel), NULL);
732             width = DrawMenu(200, 200, Green, 1, GetString(STR_Save), GetString(STR_Erase), NULL);
733             DrawMenu(200 + 5 + width, 200 + FontData->leading, Green,
734                      YesNo, GetString(STR_Yes), GetString(STR_No), NULL);
735             break;
736           case SUBSTATE_BUSY:
737             DrawProgressBar();
738             DEMOSetFontType(DM_FT_RVS);
739             DrawUsage(Slot, GetString(STR_Null), GetString(STR_Null), GetString(STR_Erasing));
740             break;
741           case SUBSTATE_WAIT:
742             DrawProgressBar();
743             DEMOSetFontType(DM_FT_RVS);
744             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Null), NULL);
745             switch (result)
746             {
747               case CARD_RESULT_READY:
748                 DrawMessage(16, 200, Green, GetString(STR_Erased));
749                 break;
750               default:
751                 DrawMessage(16, 200, Red,   GetString(STR_MayNotErased));
752                 break;
753             }
754             break;
755         }
756         break;
757 
758       case CARDUTIL_CMD_FORMAT:
759         switch (SubState)
760         {
761           case SUBSTATE_VERIFY:
762             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Cancel), NULL);
763             DrawMessage(16, 75, Red, GetString(STR_Format));
764             DrawMenu(200, 200, Green, YesNo, GetString(STR_Yes), GetString(STR_No), NULL);
765             break;
766           case SUBSTATE_BUSY:
767             DrawProgressBar();
768             DEMOSetFontType(DM_FT_RVS);
769             DrawUsage(Slot, GetString(STR_Null), GetString(STR_Null), GetString(STR_Formatting));
770             break;
771           case SUBSTATE_WAIT:
772             DrawProgressBar();
773             DEMOSetFontType(DM_FT_RVS);
774             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Null), NULL);
775             switch (result)
776             {
777               case CARD_RESULT_READY:
778                 DrawMessage(16, 200, Green, GetString(STR_Formatted));
779                 break;
780               default:
781                 DrawMessage(16, 200, Red,   GetString(STR_MayNotFormatted));
782                 break;
783             }
784             break;
785         }
786         break;
787 
788       case CARDUTIL_CMD_CHECKSPACE:
789       case CARDUTIL_CMD_IGNORE:
790       case CARDUTIL_CMD_MAIN_MENU:
791         // At the beginning of the program, it should check if the player
792         // has a memory card that can be used to save the game data.
793         switch (SubState)
794         {
795           case SUBSTATE_SELECT: // mounting...
796             DEMOSetFontType(DM_FT_RVS);
797             DrawUsage(Slot, GetString(STR_Null), GetString(STR_Null), GetString(STR_Mounting));
798             break;
799           case SUBSTATE_COMMAND:
800             DEMOSetFontType(DM_FT_RVS);
801             DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Cancel), NULL);
802             DrawMessage(16, 75, Red, GetString(STR_InsSpace));
803             DrawMenu(200, 200, Green, Command - CARDUTIL_CMD_CHECKSPACE,
804                      GetString(STR_Cancel), GetString(STR_Ignore), GetString(STR_Menu), NULL);
805             break;
806         }
807         break;
808 
809       case CARDUTIL_CMD_ERROR:
810         DrawUsage(Slot, GetString(STR_Confirm), GetString(STR_Null), NULL);
811         DrawMessage(16, 69, Red, GetString(STR_Broken));
812         break;
813 
814       default:
815         DEMOSetFontType(DM_FT_RVS);
816         DrawUsage(Slot, GetString(STR_Select), GetString(STR_Finish), NULL);
817         break;
818     }
819 }
820 
821 /*---------------------------------------------------------------------------*
822   Name:         ListDrawTick
823 
824   Description:  Draw card file directory
825  *---------------------------------------------------------------------------*/
ListDrawTick(void)826 static void ListDrawTick(void)
827 {
828     GXRenderModeObj* rmp;
829     CardUtilDirent*  ent;
830     int              i;
831     s32              numFiles;
832     s32              numBlocks;
833 
834     // Locks directory to prevents card utility thread updates directory behind
835     numFiles = CardUtilLockDirectory();
836 
837     // Check if new files can be created.
838     NumEntries = numFiles;
839     numBlocks = GetBlockSize(FILE_SIZE);
840     if (0 < CardUtilFilesNotUsed() &&
841         2 * numBlocks <= CardUtilBlocksNotUsed())
842     {
843         // Last directory entry is used for creating a new data file.
844         ++NumEntries;
845     }
846 
847     rmp = DEMOGetRenderModeObj();
848     DEMOInitCaption(DM_FT_XLU, (s16) rmp->fbWidth, (s16) rmp->efbHeight);
849 
850     // Updates Current as card utility thread may have deleted directory entries
851     if (NumEntries <= Current && 0 < NumEntries)
852     {
853         Current = NumEntries - 1;
854         if (Current < Offset)
855         {
856             Offset = Current;
857         }
858     }
859 
860     // Draw directory
861     for (i = 0; i < LIST_ROW && Offset + i < NumEntries; ++i)
862     {
863         DEMOSetFontType((Current == Offset + i) ? DM_FT_RVS : DM_FT_XLU);
864 
865         if (Offset + i == numFiles)
866         {
867             // The last entry for creating new file
868             DEMORFPrintf(60, (s16) (75 + i * 34), 0, "%s (%d)",
869                          GetString(STR_Open),
870                          CardUtilBlocksNotUsed());
871             continue;
872         }
873 
874         ent = &Directory[Offset + i];
875 
876         // Note in GameCube file viewer, fileNo and fileName do not appear
877         // on screen. This is program verification only.
878         DEMORFPrintf(60, (s16) (75 + i * 34), 0, "%-.32s (#%d, %d blocks)",
879                   ent->stat.fileName,
880                   ent->fileNo,
881                   ent->stat.length / CardUtilSectorSize());
882 
883         // Draw animated icon
884         CardUtilDrawAnimatedIcon(ent, 16, 75 + i * 34 - 29, CARD_ICON_WIDTH);
885     }
886     DEMOSetROMFontSize(20, -1);
887     DEMOSetFontType(DM_FT_XLU);
888     DEMORFPrintf(180, 430, 0, GetString(STR_Stat),
889                  CardUtilByteNotUsed() / CardUtilSectorSize(),
890                  CardUtilFilesNotUsed());
891 
892     if (Current < numFiles)
893     {
894         // Show more detailed current file info
895         ent = &Directory[Current];
896 
897         // Draw comment:
898         // Note in GameCube file viewer, not all the characters appears on screen.
899         // Effective areas are shown as green boxes.
900 
901         // 'titl', mem_lay 16h, -1, 1420h,
902         DEMOSetFontType(DM_FT_OPQ);
903         DrawRectangle(180, 354 - FontData->ascent, 322, FontData->leading, Green);
904         DEMOSetFontType(DM_FT_XLU);
905         DEMOSetROMFontSize(22, -1);
906         DEMORFPrintf(180, 354, 0, "%-32.32s", ent->comment);
907 
908         // 'info', mem_lay 14h, -1, 10d0h
909         DEMOSetFontType(DM_FT_OPQ);
910         DrawRectangle(180, 378 - FontData->ascent, 269, FontData->leading, Green);
911         DEMOSetFontType(DM_FT_XLU);
912         DEMOSetROMFontSize(20, -1);
913         DEMORFPrintf(180, 378, 0, "%-32.32s", &ent->comment[32]);
914         DEMOSetROMFontSize((s16) FontData->cellWidth, -1);
915 
916         // Draw banner
917         ent = &Directory[Current];
918         if (CARDGetBannerFormat(&ent->stat) != CARD_STAT_BANNER_NONE)
919         {
920             DEMOSetFontType(DM_FT_OPQ);
921             DrawRectangle(16, 330, DVD_BANNER_WIDTH, DVD_BANNER_HEIGHT, Black);
922             CardUtilDrawIcon(16, 330, CARD_BANNER_WIDTH,
923                      (u8*) ent + ent->stat.offsetBanner     - ent->stat.iconAddr,
924                      (u8*) ent + ent->stat.offsetBannerTlut - ent->stat.iconAddr,
925                      CARD_BANNER_WIDTH, CARD_BANNER_HEIGHT,
926                      CARDGetBannerFormat(&ent->stat));
927         }
928 
929         // Draw all icons for program verification purpose only
930         for (i = 0; i < CARD_ICON_MAX; ++i)
931         {
932             if (CARDGetIconFormat(&ent->stat, i) != CARD_STAT_ICON_NONE)
933             {
934                 CardUtilDrawIcon((s16) (16 + (i % 4) * 36), (s16) (330 + 42 + (i / 4) * 36), CARD_ICON_WIDTH,
935                          (u8*) ent + ent->stat.offsetIcon[i]  - ent->stat.iconAddr,
936                          (u8*) ent + ent->stat.offsetIconTlut - ent->stat.iconAddr,
937                          CARD_ICON_WIDTH, CARD_ICON_HEIGHT,
938                          CARDGetIconFormat(&ent->stat, i));
939             }
940         }
941 
942         // Draw animated icon
943         CardUtilDrawAnimatedIcon(ent, 16 + 3 * 36, 330, CARD_ICON_WIDTH);
944     }
945 
946     // Unlocks directory so card utility thread can updates directory
947     CardUtilUnlockDirectory();
948 
949     // Draw instructions and popup menu
950     DrawInstructions();
951 }
952 
953 /*---------------------------------------------------------------------------*
954   Name:         NewFileName
955 
956   Description:  Generates a new file name. This function is only for
957                 illustration (no good). XXX
958  *---------------------------------------------------------------------------*/
NewFileName(char * fileName)959 static void NewFileName(char* fileName)
960 {
961     OSTime         time;
962     OSCalendarTime ct;
963 
964     time = OSGetTime();
965     OSTicksToCalendarTime(time, &ct);
966     sprintf(fileName, "%04d/%02d/%02d %02d:%02d:%02d",
967             ct.year, ct.mon + 1, ct.mday,
968             ct.hour, ct.min, ct.sec);
969 }
970 
971 /*---------------------------------------------------------------------------*
972   Name:         SaveData
973 
974   Description:  Layouts banner, icon and comment data and save the file.
975 
976   Arguments:    fileName    filename whose length should be less than
977                             CARD_FILENAME_MAX 'cause SaveData() creates
978                             a temporary filename by adding '~' before the
979                             head of the filename.
980                 banner      pointer to DVDBanner data
981                 tplBanner   banner texture image (NULL to use DVDbanner data)
982                 tplIcons    icon texture images (up to eight)
983                 iconSpeed   one of CARD_STAT_SPEED_*. Though each icon can
984                             use different icon speed if desired, SaveData()
985                             assigns the same speed for all icons.
986                 iconAnim    one of CARD_STAT_ANIM_*
987                 description game description (32 bytes)
988 
989   Note:         Use tplBanner to specify C8 texture image to save
990                 the game file size. Otherwise use DVDBanner image.
991                 In either case, the graphics should be same as
992                 the DVDBanner image.
993 
994   Returns:      Pointer to game data save area (after icon data)
995  *---------------------------------------------------------------------------*/
SaveData(char * fileName,DVDBanner * banner,TPLPalettePtr tplBanner,TPLPalettePtr tplIcons,u16 iconSpeed,u8 iconAnim,char * description)996 static void* SaveData(char* fileName, DVDBanner* banner,
997                       TPLPalettePtr tplBanner,
998                       TPLPalettePtr tplIcons, u16 iconSpeed, u8 iconAnim,
999                       char* description)
1000 {
1001     TPLPalettePtr    tpl = 0;
1002     TPLDescriptorPtr tdp;
1003     int              i;
1004     u8*              ptr;
1005 
1006     ASSERT(banner);
1007 
1008     iconSpeed &= CARD_STAT_SPEED_MASK;
1009     iconAnim  &= CARD_STAT_ANIM_MASK;
1010 
1011     memset(&CardStatTemplate, 0, sizeof CardStatTemplate);
1012     CardStatTemplate.length = (u32) GetBlockSize(FILE_SIZE) * CardUtilSectorSize();
1013 
1014     ASSERT(CardData == 0);
1015     CardData = OSAlloc(CardStatTemplate.length);
1016     ASSERT(CardData);
1017     memset(CardData, 0, CardStatTemplate.length);
1018     strncpy(CardStatTemplate.fileName, fileName, CARD_FILENAME_MAX);
1019 
1020     ptr = (u8*) CardData;
1021 
1022     // Comment
1023     CardStatTemplate.commentAddr = 0;
1024     strncpy((char*) ptr, (char*) banner->shortTitle, CARD_COMMENT_SIZE / 2);
1025     ptr += CARD_COMMENT_SIZE / 2;
1026     strncpy((char*) ptr, description,        CARD_COMMENT_SIZE / 2);
1027     ptr += CARD_COMMENT_SIZE / 2;
1028 
1029     // Banner
1030     CardStatTemplate.iconAddr = (u32) (ptr - (u8*) CardData);
1031     if (tplBanner == NULL)
1032     {
1033         // Use DVDBanner image
1034         CardStatTemplate.bannerFormat = CARD_STAT_BANNER_RGB5A3;
1035         memcpy(ptr, banner->image,
1036                2 * CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT);
1037         ptr += 2 * CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
1038     }
1039     else
1040     {
1041         tdp = TPLGet(tplBanner, 0);
1042 
1043         // Update bannerFormat
1044         switch ((GXTexFmt) tdp->textureHeader->format)
1045         {
1046           case GX_TF_RGB5A3:
1047             CardStatTemplate.bannerFormat = CARD_STAT_BANNER_RGB5A3;
1048             break;
1049           case GX_TF_C8:
1050             CardStatTemplate.bannerFormat = CARD_STAT_BANNER_C8;
1051             ASSERT(tdp->CLUTHeader);
1052             break;
1053           default:
1054             OSHalt("Unsupported banner texture formant.");
1055             break;
1056         }
1057 
1058         // Copy texture image and tlut
1059         if (CARDGetBannerFormat(&CardStatTemplate) == CARD_STAT_BANNER_RGB5A3)
1060         {
1061             memcpy(ptr, tdp->textureHeader->data,
1062                    2 * CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT);
1063             ptr += 2 * CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
1064         }
1065         else    // CARD_STAT_ICON_C8
1066         {
1067             ASSERT(tdp->CLUTHeader);
1068             ASSERT((GXTlutFmt) tdp->CLUTHeader->format == GX_TL_RGB5A3);
1069             ASSERT(tdp->CLUTHeader->numEntries == 256);
1070 
1071             memcpy(ptr, tdp->textureHeader->data,
1072                    CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT);
1073             ptr += CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
1074             memcpy(ptr, tdp->CLUTHeader->data, 2 * 256);
1075             ptr += 2 * 256;
1076         }
1077     }
1078 
1079     // Icon
1080     if (tplIcons)
1081     {
1082         int iconTlut;
1083 
1084         // Update iconFormat
1085         for (i = 0; i < tplIcons->numDescriptors && i < CARD_ICON_MAX; i++)
1086         {
1087             tdp = TPLGet(tplIcons, (u32) i);
1088 
1089             switch ((GXTexFmt) tdp->textureHeader->format)
1090             {
1091               case GX_TF_RGB5A3:
1092                 CARDSetIconFormat(&CardStatTemplate, i, CARD_STAT_ICON_RGB5A3);
1093                 break;
1094               case GX_TF_C8:
1095                 CARDSetIconFormat(&CardStatTemplate, i, CARD_STAT_ICON_C8);
1096                 ASSERT(tdp->CLUTHeader);
1097                 break;
1098               default:
1099                 OSHalt("Unsupported icon texture formant.");
1100                 break;
1101             }
1102             CARDSetIconSpeed(&CardStatTemplate, i, iconSpeed);
1103         }
1104         for (; i < CARD_ICON_MAX; i++)
1105         {
1106             CARDSetIconSpeed(&CardStatTemplate, i, CARD_STAT_SPEED_END);
1107         }
1108 
1109         CardStatTemplate.bannerFormat |= iconAnim;
1110 
1111         // Copy icon texture images and tlut
1112         iconTlut = -1;
1113         for (i = 0; i < tplIcons->numDescriptors && i < CARD_ICON_MAX; i++)
1114         {
1115             tdp = TPLGet(tplIcons, (u32) i);
1116             switch (CARDGetIconFormat(&CardStatTemplate, i))
1117             {
1118               case CARD_STAT_ICON_RGB5A3:
1119                 memcpy(ptr, tdp->textureHeader->data,
1120                        2 * CARD_ICON_WIDTH * CARD_ICON_HEIGHT);
1121                 ptr += 2 * CARD_ICON_WIDTH * CARD_ICON_HEIGHT;
1122                 break;
1123               case CARD_STAT_ICON_C8:
1124                 ASSERT(tdp->CLUTHeader);
1125                 ASSERT((GXTlutFmt) tdp->CLUTHeader->format == GX_TL_RGB5A3);
1126                 ASSERT(tdp->CLUTHeader->numEntries == 256);
1127 
1128                 memcpy(ptr, tdp->textureHeader->data,
1129                        CARD_ICON_WIDTH * CARD_ICON_HEIGHT);
1130                 ptr += CARD_ICON_WIDTH * CARD_ICON_HEIGHT;
1131                 iconTlut = i;
1132                 break;
1133             }
1134         }
1135         if (0 <= iconTlut)
1136         {
1137             tdp = TPLGet(tplIcons, (u32) i);
1138             memcpy(ptr, tdp->CLUTHeader->data, 2 * 256);
1139             ptr += 2 * 256;
1140         }
1141     }
1142 
1143     return ptr;
1144 }
1145 
1146 /*---------------------------------------------------------------------------*
1147   Name:         SelectAnimTick
1148 
1149   Description:  Selects the directory entry.
1150                 Starts command selection if A button is pushed.
1151                 Goes back to slot selection if B button is pushed.
1152  *---------------------------------------------------------------------------*/
SelectAnimTick(void)1153 static void SelectAnimTick(void)
1154 {
1155     if (Conts[4].repeat & PAD_BUTTON_UP)
1156     {
1157         if (0 < Current)
1158         {
1159             --Current;
1160             if (Current < Offset)
1161             {
1162                 --Offset;
1163             }
1164         }
1165     }
1166     else if (Conts[4].repeat & PAD_BUTTON_DOWN)
1167     {
1168         if (Current < NumEntries - 1)
1169         {
1170             ++Current;
1171             if (Offset + LIST_ROW <= Current)
1172             {
1173                 ++Offset;
1174             }
1175         }
1176     }
1177     else if (Conts[4].down & PAD_BUTTON_B)
1178     {
1179         CardUtilUnmount(Slot);
1180         State = STATE_PROBE;
1181     }
1182     else if (Conts[4].down & PAD_BUTTON_A)
1183     {
1184         SubState = SUBSTATE_COMMAND;
1185         Command = CARDUTIL_CMD_SAVE;
1186     }
1187 
1188 }
1189 
1190 /*---------------------------------------------------------------------------*
1191   Name:         CommandAnimTick
1192 
1193   Description:  Switches the command.
1194                 Selects the current command if A button is pushed.
1195                 Goes back to file selection if B button is pushed.
1196  *---------------------------------------------------------------------------*/
CommandAnimTick(void)1197 static void CommandAnimTick(void)
1198 {
1199     if (Conts[4].down & PAD_BUTTON_B)
1200     {
1201         switch (Command)
1202         {
1203           case CARDUTIL_CMD_SAVE:
1204           case CARDUTIL_CMD_ERASE:
1205             SubState = SUBSTATE_SELECT;
1206             Command = CARDUTIL_CMD_NONE;
1207             break;
1208           case CARDUTIL_CMD_CHECKSPACE:
1209           case CARDUTIL_CMD_IGNORE:
1210           case CARDUTIL_CMD_MAIN_MENU:
1211             CardUtilUnmount(Slot);
1212             State = STATE_PROBE;
1213             break;
1214         }
1215     }
1216     else if (Conts[4].repeat & PAD_BUTTON_UP)
1217     {
1218         switch (Command)
1219         {
1220           case CARDUTIL_CMD_SAVE:
1221           case CARDUTIL_CMD_ERASE:
1222             Command = CARDUTIL_CMD_SAVE;
1223             break;
1224           case CARDUTIL_CMD_CHECKSPACE:
1225           case CARDUTIL_CMD_IGNORE:
1226           case CARDUTIL_CMD_MAIN_MENU:
1227             if (CARDUTIL_CMD_CHECKSPACE < Command)
1228             {
1229                 --Command;
1230             }
1231             break;
1232         }
1233     }
1234     else if (Conts[4].repeat & PAD_BUTTON_DOWN)
1235     {
1236         switch (Command)
1237         {
1238           case CARDUTIL_CMD_SAVE:
1239           case CARDUTIL_CMD_ERASE:
1240             Command = CARDUTIL_CMD_ERASE;
1241             break;
1242           case CARDUTIL_CMD_CHECKSPACE:
1243           case CARDUTIL_CMD_IGNORE:
1244           case CARDUTIL_CMD_MAIN_MENU:
1245             if (Command < CARDUTIL_CMD_MAIN_MENU)
1246             {
1247                 ++Command;
1248             }
1249             break;
1250         }
1251     }
1252     else if (Conts[4].down & PAD_BUTTON_A)
1253     {
1254         YesNo = NO;
1255         switch (Command)
1256         {
1257           case CARDUTIL_CMD_SAVE:
1258             // Do not issue save if no space is available
1259             if (IsSpaceAvailable(FILE_SIZE))
1260             {
1261                 SubState = SUBSTATE_VERIFY;
1262             }
1263             break;
1264           case CARDUTIL_CMD_ERASE:
1265             // Do not issue erase if no file is selected
1266             if (Current < CardUtilNumFiles())
1267             {
1268                 SubState = SUBSTATE_VERIFY;
1269             }
1270             break;
1271           case CARDUTIL_CMD_CHECKSPACE:
1272             CardUtilUnmount(Slot);
1273             State = STATE_PROBE;
1274             break;
1275           case CARDUTIL_CMD_IGNORE:
1276             Command = CARDUTIL_CMD_NONE;
1277             SubState = SUBSTATE_SELECT;
1278             break;
1279           case CARDUTIL_CMD_MAIN_MENU:
1280             ForceMenu = TRUE;
1281             break;
1282         }
1283     }
1284 }
1285 
1286 /*---------------------------------------------------------------------------*
1287   Name:         VerifyAnimTick
1288 
1289   Description:  Starts the current command if A button is pushed.
1290                 Cancels the current command if B button is pushed.
1291 
1292                 If both A and B buttons are pushed, B button has a priority.
1293  *---------------------------------------------------------------------------*/
VerifyAnimTick(void)1294 static void VerifyAnimTick(void)
1295 {
1296     char fileName[CARD_FILENAME_MAX + 1];
1297     u8*  ptr;
1298 
1299     if (Conts[4].down & PAD_BUTTON_B)
1300     {
1301         switch (Command)
1302         {
1303           case CARDUTIL_CMD_SAVE:
1304           case CARDUTIL_CMD_ERASE:
1305             SubState = SUBSTATE_COMMAND;
1306             break;
1307           case CARDUTIL_CMD_FORMAT:
1308           default:
1309             CardUtilUnmount(Slot);
1310             State = STATE_PROBE;
1311             break;
1312         }
1313     }
1314     else if (Conts[4].repeat & PAD_BUTTON_UP)
1315     {
1316         YesNo = YES;
1317     }
1318     else if (Conts[4].repeat & PAD_BUTTON_DOWN)
1319     {
1320         YesNo = NO;
1321     }
1322     else if (Conts[4].down & PAD_BUTTON_A)
1323     {
1324         switch (YesNo)
1325         {
1326           case YES:
1327             switch (Command)
1328             {
1329               case CARDUTIL_CMD_SAVE:
1330                 fileName[CARD_FILENAME_MAX] = '\0';
1331                 if (CardUtilNumFiles() <= Current)
1332                 {
1333                     // Create new file name
1334                     NewFileName(fileName);
1335                 }
1336                 else
1337                 {
1338                     // Keep the current name
1339                     strncpy(fileName, Directory[Current].stat.fileName, CARD_FILENAME_MAX);
1340                 }
1341                 ptr = SaveData(fileName, Banner,
1342                                NULL,
1343                                TplIcons, CARD_STAT_SPEED_MIDDLE, CARD_STAT_ANIM_LOOP,
1344                                "File Description");
1345 
1346                 //
1347                 //  Implement your code here
1348                 //
1349                 //  Now ptr points to the game data save area after comment,
1350                 //  banner and icon data.
1351                 //
1352 
1353                 CardUtilSave(Slot, &CardStatTemplate, CardData);
1354                 SubState = SUBSTATE_BUSY;
1355                 break;
1356               case CARDUTIL_CMD_ERASE:
1357                 CardUtilErase(Slot, Directory[Current].fileNo);
1358                 SubState = SUBSTATE_BUSY;
1359                 break;
1360               case CARDUTIL_CMD_FORMAT:
1361                 CardUtilFormat(Slot);
1362                 SubState = SUBSTATE_BUSY;
1363                 break;
1364             }
1365             break;
1366           case NO:
1367             switch (Command)
1368             {
1369               case CARDUTIL_CMD_FORMAT:
1370                 CardUtilUnmount(Slot);
1371                 State = STATE_PROBE;
1372                 break;
1373               default:
1374                 Command = CARDUTIL_CMD_NONE;
1375                 SubState = SUBSTATE_SELECT;
1376                 break;
1377             }
1378             break;
1379         }
1380     }
1381 }
1382 
1383 /*---------------------------------------------------------------------------*
1384   Name:         BusyAnimTick
1385 
1386   Description:  The function is called when card utility thread gets not-busy.
1387                 Moves on to confirm screen.
1388  *---------------------------------------------------------------------------*/
BusyAnimTick(void)1389 static void BusyAnimTick(void)
1390 {
1391     SubState = SUBSTATE_WAIT;
1392     BeginWaitAt = OSGetTime();  // Save current time so confirm screen can
1393                                 // time-out if desired.
1394     switch (Command)
1395     {
1396       case CARDUTIL_CMD_SAVE:
1397         OSFree(CardData);
1398         CardData = 0;
1399         break;
1400     }
1401 }
1402 
1403 /*---------------------------------------------------------------------------*
1404   Name:         WaitAnimTick
1405 
1406   Description:  Goes back to default state after the player confirmed.
1407                 Times out in 1.5 sec if the current command was successfully
1408                 executed.
1409  *---------------------------------------------------------------------------*/
WaitAnimTick(s32 result)1410 static void WaitAnimTick(s32 result)
1411 {
1412     if (!((Conts[4].down & (PAD_BUTTON_A | PAD_BUTTON_B)) ||
1413           (OSMillisecondsToTicks(1500) < OSGetTime() - BeginWaitAt &&
1414            result == CARD_RESULT_READY)))
1415     {
1416         return;
1417     }
1418 
1419     switch (Command)
1420     {
1421       case CARDUTIL_CMD_ERROR:
1422         CardUtilUnmount(Slot);
1423         State = STATE_PROBE;
1424         break;
1425       default:
1426         Command = CARDUTIL_CMD_NONE;
1427         SubState = SUBSTATE_SELECT;
1428         break;
1429     }
1430 }
1431 
1432 /*---------------------------------------------------------------------------*
1433   Name:         ListAnimTick
1434 
1435   Description:  Top level animation tick function for the directory screen
1436  *---------------------------------------------------------------------------*/
ListAnimTick(void)1437 static void ListAnimTick(void)
1438 {
1439     s32 result;
1440 
1441     result = CardUtilResultCode();
1442 
1443     // Listup
1444     if (!List && result == CARD_RESULT_READY)
1445     {
1446         List = TRUE;
1447         CardUtilList(Slot, Directory);
1448         result = CardUtilResultCode();
1449     }
1450 
1451     // The current command must not be aborted.
1452     if (result == CARD_RESULT_BUSY)
1453     {
1454         return;
1455     }
1456 
1457     // Check space
1458     if (Command == CARDUTIL_CMD_CHECKSPACE)
1459     {
1460         if (IsSpaceAvailable(FILE_SIZE))
1461         {
1462             Command = CARDUTIL_CMD_NONE;
1463             SubState = SUBSTATE_SELECT;
1464         }
1465         else if (result == CARD_RESULT_READY)
1466         {
1467             SubState = SUBSTATE_COMMAND;
1468         }
1469     }
1470 
1471     if (SubState < SUBSTATE_BUSY && !CARDProbe(Slot))
1472     {
1473         State = STATE_PROBE;
1474         return;
1475     }
1476 
1477     // Process error
1478     if (SubState < SUBSTATE_VERIFY)
1479     {
1480         switch (result)
1481         {
1482           case CARD_RESULT_READY:
1483           case CARD_RESULT_NOFILE:
1484           case CARD_RESULT_EXIST:
1485           case CARD_RESULT_NOPERM:
1486           case CARD_RESULT_LIMIT:
1487           case CARD_RESULT_NAMETOOLONG:
1488           case CARD_RESULT_CANCELED:
1489             break;
1490 
1491           case CARD_RESULT_NOENT:
1492           case CARD_RESULT_INSSPACE:
1493             break;
1494 
1495           case CARD_RESULT_BROKEN:
1496           case CARD_RESULT_ENCODING:
1497             Command = CARDUTIL_CMD_FORMAT;
1498             YesNo = NO;
1499             SubState = SUBSTATE_VERIFY;
1500             return;
1501 
1502           case CARD_RESULT_NOCARD:
1503             State = STATE_PROBE;
1504             return;
1505 
1506           default:
1507           case CARD_RESULT_IOERROR:
1508           case CARD_RESULT_FATAL_ERROR:
1509           case CARD_RESULT_WRONGDEVICE:
1510             Command = CARDUTIL_CMD_ERROR;
1511             SubState = SUBSTATE_WAIT;
1512             BeginWaitAt = OSGetTime();
1513             return;
1514         }
1515     }
1516 
1517     switch (SubState)
1518     {
1519       case SUBSTATE_SELECT:
1520         SelectAnimTick();
1521         break;
1522       case SUBSTATE_COMMAND:
1523         CommandAnimTick();
1524         break;
1525       case SUBSTATE_VERIFY:
1526         VerifyAnimTick();
1527         break;
1528       case SUBSTATE_BUSY:
1529         BusyAnimTick();
1530         break;
1531       case SUBSTATE_WAIT:
1532         WaitAnimTick(result);
1533         break;
1534     }
1535 }
1536 
1537 /*---------------------------------------------------------------------------*
1538   Name:         LoadBanner
1539 
1540   Description:  Load banner file "/opening.bnr"
1541  *---------------------------------------------------------------------------*/
LoadBanner(void)1542 static DVDBanner* LoadBanner(void)
1543 {
1544     static DVDBanner banner ATTRIBUTE_ALIGN(32);
1545     DVDFileInfo      fileInfo;
1546     BOOL             result;
1547     s32              length;
1548 
1549     result = DVDOpen("/" DVD_BANNER_FILENAME, &fileInfo);
1550     if (!result)
1551         return NULL;
1552     length = (s32) OSRoundUp32B(DVDGetLength(&fileInfo));
1553     if (length < sizeof(DVDBanner))
1554         return NULL;
1555     result = DVDRead(&fileInfo, &banner, sizeof(DVDBanner), 0);
1556     if (!result)
1557         return NULL;
1558     if (banner.id != DVD_BANNER_ID)
1559         return NULL;
1560     return &banner;
1561 }
1562 
1563 /*---------------------------------------------------------------------------*
1564   Name:         main
1565 
1566   Description:  After loading ROM font and banner and icon textures,
1567                 goes into the main loop.
1568  *---------------------------------------------------------------------------*/
main(void)1569 int main(void)
1570 {
1571     static BOOL reset = FALSE;  // TRUE if reset is requested
1572 
1573     DEMOInit(NULL);
1574     FontData = DEMOInitROMFont();  // Use IPL ROM font
1575     if (FontData == 0)
1576     {
1577         OSHalt("This program requires boot ROM ver 0.8 or later\n");
1578         // NOT REACHED HERE
1579     }
1580 
1581     InitCont(0, FALSE);
1582 
1583     CardUtilInit(CardStack + sizeof(CardStack), sizeof(CardStack), 18);
1584 
1585     // Clear EFB
1586     GXSetCopyClear(Blue, 0x00ffffff);
1587     GXCopyDisp(DEMOGetCurrentBuffer(), GX_TRUE);
1588 
1589     // Load banner file "/opening.bnr"
1590     Banner = LoadBanner();
1591     if (Banner == 0)
1592     {
1593         OSHalt("\"" DVD_BANNER_FILENAME "\" is not found in the root directory.\n");
1594         // NOT REACHED HERE
1595     }
1596 
1597     // Load banner and icon texture palettes
1598     //   banner.tpl should contain a single 96x32 C8 or RGB5A3 texture image.
1599     //   icon.tpl should contain from one to eight 32x32 C8 or RGB5A3 texture images.
1600     TPLGetPalette(&TplBanner, "/carddemo/banner.tpl");
1601     TPLGetPalette(&TplIcons,  "/carddemo/icon.tpl");
1602 
1603     for (;;)
1604     {
1605         DEMOBeforeRender();
1606         ReadCont();
1607         InterfaceTable[State].DrawTick();
1608         InterfaceTable[State].AnimTick();
1609         DEMODoneRender();
1610 
1611         if (ForceMenu && SubState != SUBSTATE_BUSY)     // Do not reset while saving data, etc.
1612         {
1613             // Use hot reset to start GameCube main menu
1614             OSResetSystem(OS_RESET_HOTRESET, 0x01, TRUE);
1615         }
1616 
1617         // Handle reset button
1618         if (OSGetResetButtonState())
1619         {
1620             reset = TRUE;
1621         }
1622         else if (reset && SubState != SUBSTATE_BUSY)    // Do not reset while saving data, etc.
1623         {
1624             // Restart
1625             OSResetSystem(OS_RESET_RESTART, 0x00, FALSE);
1626         }
1627     }
1628 
1629     return 0;   // lint
1630 }
1631