/**************************************************************************** * Visual Boy Advance GX * * Tantric September 2008 * * menudraw.cpp * * Menu drawing routines ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #include "video.h" #include "menudraw.h" #include "vba.h" #include "filesel.h" #include "dvd.h" #include "input.h" #include "networkop.h" #include "wiiusbsupport.h" #include "filelist.h" /*** Globals ***/ static FT_Library ftlibrary; static FT_Face face; static FT_GlyphSlot slot; static unsigned int fonthi, fontlo; extern int screenheight; extern unsigned int *xfb[2]; extern int whichfb; unsigned int getcolour (u8 r1, u8 g1, u8 b1); void DrawLineFast( int startx, int endx, int y, u8 r, u8 g, u8 b ); u32 getrgb( u32 ycbr, u32 low ); /**************************************************************************** * Initialisation of libfreetype ***************************************************************************/ int FT_Init () { int err; err = FT_Init_FreeType (&ftlibrary); if (err) return 1; err = FT_New_Memory_Face (ftlibrary, (FT_Byte *) font_ttf, font_ttf_size, 0, &face); if (err) return 1; setfontsize (16); setfontcolour (0xff, 0xff, 0xff); slot = face->glyph; return 0; } /**************************************************************************** * setfontsize * * Set the screen font size in pixels ***************************************************************************/ void setfontsize (int pixelsize) { int err; err = FT_Set_Pixel_Sizes (face, 0, pixelsize); if (err) printf ("Error setting pixel sizes!"); } /**************************************************************************** * DrawCharacter * Draws a single character on the screen ***************************************************************************/ static void DrawCharacter (FT_Bitmap * bmp, FT_Int x, FT_Int y) { FT_Int i, j, p, q; FT_Int x_max = x + bmp->width; FT_Int y_max = y + bmp->rows; int spos; unsigned int pixel; int c; for (i = x, p = 0; i < x_max; i++, p++) { for (j = y, q = 0; j < y_max; j++, q++) { if (i < 0 || j < 0 || i >= 640 || j >= screenheight) continue; /*** Convert pixel position to GC int sizes ***/ spos = (j * 320) + (i >> 1); pixel = xfb[whichfb][spos]; c = bmp->buffer[q * bmp->width + p]; /*** Cool Anti-Aliasing doesn't work too well at hires on GC ***/ if (c > 128) { if (i & 1) pixel = (pixel & 0xffff0000) | fontlo; else pixel = ((pixel & 0xffff) | fonthi); xfb[whichfb][spos] = pixel; } } } } /**************************************************************************** * DrawText * * Place the font bitmap on the screen ***************************************************************************/ void DrawText (int x, int y, const char *text) { int px, n; int i; int err; int value, count; n = strlen (text); if (n == 0) return; setfontcolour (0x00, 0x00, 0x00); /*** x == -1, auto centre ***/ if (x == -1) { value = 0; px = 0; } else { value = 1; px = x; } for (count = value; count < 2; count++) { /*** Draw the string ***/ for (i = 0; i < n; i++) { err = FT_Load_Char (face, text[i], FT_LOAD_RENDER); if (err) { printf ("Error %c %d\n", text[i], err); continue; /*** Skip unprintable characters ***/ } if (count) DrawCharacter (&slot->bitmap, px + slot->bitmap_left, y - slot->bitmap_top); px += slot->advance.x >> 6; } px = (640 - px) >> 1; } } /**************************************************************************** * setfontcolour * * Uses RGB triple values. ***************************************************************************/ void setfontcolour (u8 r, u8 g, u8 b) { u32 fontcolour; fontcolour = getcolour (r, g, b); fonthi = fontcolour & 0xffff0000; fontlo = fontcolour & 0xffff; } /**************************************************************************** * Draws Version # on screen ***************************************************************************/ void DrawVersion() { setfontsize (12); setfontcolour (0,0,0); char versionText[50]; sprintf(versionText, "%s %s", APPNAME, APPVERSION); DrawText (115, screenheight - 34, versionText); } /**************************************************************************** * Display credits, legal copyright and licence * * THIS MUST NOT BE REMOVED IN ANY DERIVATIVE WORK. ***************************************************************************/ void Credits () { clearscreen (); setfontcolour (0x00, 0x00, 0x00); setfontsize (26); DrawText (-1, 150, "Credits"); int ypos = 110; if (screenheight == 480) ypos += 52; else ypos += 32; setfontsize (14); DrawText (-1, ypos += 18, "Official Site: http://code.google.com/p/vba-wii/"); DrawText (90, ypos += 36, "Visual Boy Advance GX"); DrawText (380, ypos, "Tantric, Carl Kenner"); DrawText (90, ypos += 18, "GameCube/Wii Port Improvements"); DrawText (380, ypos, "emukidid"); DrawText (90, ypos += 18, "Original GameCube Port"); DrawText (380, ypos, "SoftDev"); DrawText (90, ypos += 18, "Visual Boy Advance - M"); DrawText (380, ypos, "VBA-M Team"); DrawText (90, ypos += 18, "Visual Boy Advance 1.7.2"); DrawText (380, ypos, "Forgotten"); DrawText (90, ypos += 18, "libogc"); DrawText (380, ypos, "Shagkur & wintermute"); DrawText (90, ypos += 18, "Testing"); DrawText (380, ypos, "tehskeen users"); DrawText (-1, ypos += 36, "And many others who have contributed over the years!"); setfontsize (12); DrawText (-1, ypos += 30, "This software is open source and may be copied,"); DrawText (-1, ypos += 15, "distributed, or modified under the terms of"); DrawText (-1, ypos += 15, "the GNU General Public License (GPL) Version 2."); DrawVersion(); showscreen (); } /**************************************************************************** * getcolour * * Simply converts RGB to Y1CbY2Cr format * * I got this from a pastebin, so thanks to whoever originally wrote it! ***************************************************************************/ unsigned int getcolour (u8 r1, u8 g1, u8 b1) { int y1, cb1, cr1, y2, cb2, cr2, cb, cr; u8 r2, g2, b2; r2 = r1; g2 = g1; b2 = b1; y1 = (299 * r1 + 587 * g1 + 114 * b1) / 1000; cb1 = (-16874 * r1 - 33126 * g1 + 50000 * b1 + 12800000) / 100000; cr1 = (50000 * r1 - 41869 * g1 - 8131 * b1 + 12800000) / 100000; y2 = (299 * r2 + 587 * g2 + 114 * b2) / 1000; cb2 = (-16874 * r2 - 33126 * g2 + 50000 * b2 + 12800000) / 100000; cr2 = (50000 * r2 - 41869 * g2 - 8131 * b2 + 12800000) / 100000; cb = (cb1 + cb2) >> 1; cr = (cr1 + cr2) >> 1; return ((y1 << 24) | (cb << 16) | (y2 << 8) | cr); } /**************************************************************************** * Wait for user to press A ***************************************************************************/ void WaitButtonA () { #ifdef HW_RVL while ( (PAD_ButtonsDown (0) & PAD_BUTTON_A) || (WPAD_ButtonsDown(0) & (WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A)) || (DownUsbKeys[KB_ENTER])) VIDEO_WaitVSync(); while (!(PAD_ButtonsDown (0) & PAD_BUTTON_A) && !(WPAD_ButtonsDown(0) & (WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A)) && !(DownUsbKeys[KB_ENTER])) VIDEO_WaitVSync(); #else while ( PAD_ButtonsDown (0) & PAD_BUTTON_A ) VIDEO_WaitVSync(); while (!(PAD_ButtonsDown (0) & PAD_BUTTON_A) ) VIDEO_WaitVSync(); #endif } /**************************************************************************** * Wait for user to press A or B. Returns 0 = B; 1 = A ***************************************************************************/ int WaitButtonAB () { #ifdef HW_RVL u32 gc_btns, wm_btns; while ( (PAD_ButtonsDown (0) & (PAD_BUTTON_A | PAD_BUTTON_B)) || (WPAD_ButtonsDown(0) & (WPAD_BUTTON_A | WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_B)) || (DownUsbKeys[KB_ENTER]) || (DownUsbKeys[KB_ESC]) ) VIDEO_WaitVSync(); while ( TRUE ) { gc_btns = PAD_ButtonsDown (0); wm_btns = WPAD_ButtonsDown (0); if ( (gc_btns & PAD_BUTTON_A) || (wm_btns & (WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A)) || (DownUsbKeys[KB_ENTER]) ) return 1; else if ( (gc_btns & PAD_BUTTON_B) || (wm_btns & (WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B)) || (DownUsbKeys[KB_ESC]) ) return 0; } #else u32 gc_btns; while ( (PAD_ButtonsDown (0) & (PAD_BUTTON_A | PAD_BUTTON_B)) ) VIDEO_WaitVSync(); while ( TRUE ) { gc_btns = PAD_ButtonsDown (0); if ( gc_btns & PAD_BUTTON_A ) return 1; else if ( gc_btns & PAD_BUTTON_B ) return 0; } #endif } /**************************************************************************** * Show a prompt ***************************************************************************/ void WaitPrompt (const char *msg) { int ypos = (screenheight - 64) >> 1; if (screenheight == 480) ypos += 52; else ypos += 32; clearscreen (); setfontsize(16); DrawText (-1, ypos, msg); ypos += 30; DrawText (-1, ypos, "Press A to continue"); DrawVersion(); showscreen (); WaitButtonA (); } /**************************************************************************** * Show a prompt with choice of two options. Returns 1 if A button was pressed and 0 if B button was pressed. ***************************************************************************/ int WaitPromptChoice (const char *msg, const char *bmsg, const char *amsg) { int ypos = (screenheight - 64) >> 1; if (screenheight == 480) ypos += 37; else ypos += 17; clearscreen (); setfontsize(20); DrawText (-1, ypos, msg); ypos += 60; char txt[80]; sprintf (txt, "B = %s : A = %s", bmsg, amsg); DrawText (-1, ypos, txt); DrawVersion(); showscreen (); return WaitButtonAB (); } /**************************************************************************** * Show an action in progress ***************************************************************************/ void ShowAction (const char *msg) { int ypos = (screenheight - 30) >> 1; if (screenheight == 480) ypos += 52; else ypos += 32; clearscreen (); setfontsize(20); DrawText (-1, ypos, msg); DrawVersion(); showscreen (); } /**************************************************************************** * Generic Menu Routines ***************************************************************************/ void DrawMenu (char items[][50], const char *title, int maxitems, int selected, int fontsize, int x) { int i, w = 0; int ypos = 0; int n = 1; int line_height; ypos = 105; if (screenheight == 480) ypos += 52; else ypos += 42; clearscreen (); setfontcolour (0, 0, 0); if (title != NULL) { setfontsize (26); DrawText (-1, 150, title); } // Draw menu items setfontsize (fontsize); // set font size line_height = (fontsize + 8); for (i = 0; i < maxitems; i++) { if(strlen(items[i]) > 0) { if ( items[i] == NULL ) ypos -= line_height; else if (i == selected) { for( w = 0; w < line_height; w++ ) DrawLineFast( 77, 575, n * line_height + (ypos-line_height+6) + w, 0x00, 0x00, 0x00 ); //setfontcolour (0xff, 0xff, 0xff); DrawText (x, n * line_height + ypos, items[i]); //setfontcolour (0x00, 0x00, 0x00); } else { DrawText (x, n * line_height + ypos, items[i]); } n++; } } DrawVersion(); showscreen (); } /**************************************************************************** * FindMenuItem * * Help function to find the next visible menu item on the list * Supports menu wrap-around ***************************************************************************/ int FindMenuItem(char items[][50], int maxitems, int currentItem, int direction) { int nextItem = currentItem + direction; if(nextItem < 0) nextItem = maxitems-1; else if(nextItem >= maxitems) nextItem = 0; if(strlen(items[nextItem]) > 0) return nextItem; else return FindMenuItem(&items[0], maxitems, nextItem, direction); } /**************************************************************************** * RunMenu * * Call this with the menu array defined in menu.cpp * It's here to keep all the font / interface stuff together. ***************************************************************************/ int menu = 0; int RunMenu (char items[][50], int maxitems, const char *title, int fontsize, int x) { int redraw = 1; int quit = 0; int ret = 0; u32 p = 0; u32 wp = 0; signed char gc_ay = 0; signed char wm_ay = 0; while (quit == 0) { #ifdef HW_RVL if(updateFound) { updateFound = WaitPromptChoice("An update is available!", "Update later", "Update now"); if(updateFound) if(DownloadUpdate()) ExitToLoader(); } if(ShutdownRequested) ShutdownWii(); #endif if (redraw) { DrawMenu (&items[0], title, maxitems, menu, fontsize); redraw = 0; } gc_ay = PAD_StickY (0); p = PAD_ButtonsDown (0); #ifdef HW_RVL wm_ay = WPAD_Stick (0,0,1); wp = WPAD_ButtonsDown (0); #endif VIDEO_WaitVSync(); // slow things down a bit so we don't overread the pads /*** Look for up ***/ if ( (p & PAD_BUTTON_UP) || (wp & (WPAD_BUTTON_UP | WPAD_CLASSIC_BUTTON_UP)) || (gc_ay > PADCAL) || (wm_ay > PADCAL) #ifdef HW_RVL || DownUsbKeys[KB_UP] #endif ) { redraw = 1; menu = FindMenuItem(&items[0], maxitems, menu, -1); } /*** Look for down ***/ if ( (p & PAD_BUTTON_DOWN) || (wp & (WPAD_BUTTON_DOWN | WPAD_CLASSIC_BUTTON_DOWN)) || (gc_ay < -PADCAL) || (wm_ay < -PADCAL) #ifdef HW_RVL || DownUsbKeys[KB_DOWN] #endif ) { redraw = 1; menu = FindMenuItem(&items[0], maxitems, menu, +1); } if ((p & PAD_BUTTON_A) || (wp & (WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A)) #ifdef HW_RVL || DownUsbKeys[KB_ENTER] #endif ) { quit = 1; ret = menu; } if ((p & PAD_BUTTON_B) || (wp & (WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B)) #ifdef HW_RVL || DownUsbKeys[KB_ESC] #endif ) { quit = -1; ret = -1; } } /*** Wait for B button to be released before proceeding ***/ while ( (PAD_ButtonsDown(0) & PAD_BUTTON_B) #ifdef HW_RVL || (WPAD_ButtonsDown(0) & (WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B)) || DownUsbKeys[KB_ESC] #endif ) { ret = -1; VIDEO_WaitVSync(); } return ret; } /**************************************************************************** * Showfile screen * * Display the file selection to the user ***************************************************************************/ void ShowFiles (BROWSERENTRY * browserList, int maxfiles, int offset, int selection) { int i, j; char text[MAXPATHLEN]; int ypos; int w; clearscreen (); setfontsize (26); DrawText (-1, 150, "Choose Game"); setfontsize(18); ypos = (screenheight - ((PAGESIZE - 1) * 20)) >> 1; if (screenheight == 480) ypos += 24; else ypos += 10; ypos += 30; j = 0; for (i = offset; i < (offset + PAGESIZE) && (i < maxfiles); i++) { if (browserList[i].isdir) // if a dir { strcpy (text, "["); strcat (text, browserList[i].displayname); strcat (text, "]"); } else { sprintf(text, browserList[i].displayname); } if (j == (selection - offset)) { /*** Highlighted text entry ***/ for ( w = 0; w < 20; w++ ) DrawLineFast( 77, 575, ( j * 20 ) + (ypos-16) + w, 0x00, 0x00, 0x00 ); DrawText (100, (j * 20) + ypos, text); } else { /*** Normal entry ***/ DrawText (100, (j * 20) + ypos, text); } j++; } DrawVersion(); showscreen (); } /**************************************************************************** * Cheats screen * * Displays a scrollable list of cheats to the user ***************************************************************************/ void ShowCheats (char items[][50], char itemvalues[][50], int maxitems, int offset, int selection) { int i, j = 0; int ypos; int w; clearscreen (); setfontsize (26); DrawText (-1, 150, "Cheats"); setfontsize(18); ypos = (screenheight - ((PAGESIZE - 1) * 20)) >> 1; if (screenheight == 480) ypos += 24; else ypos += 10; for (i = offset; i < (offset + PAGESIZE) && (i < maxitems); i++) { if (i == selection) { /*** Highlighted text entry ***/ for ( w = 0; w < 20; w++ ) DrawLineFast( 30, 610, ( j * 20 ) + (ypos-16) + w, 0x80, 0x80, 0x80 ); DrawText (150, (j * 20) + ypos, items[i]); DrawText (400, (j * 20) + ypos, itemvalues[i]); } else { /*** Normal entry ***/ DrawText (150, (j * 20) + ypos, items[i]); DrawText (400, (j * 20) + ypos, itemvalues[i]); } j++; } DrawVersion(); showscreen (); } /**************************************************************************** * DrawLine * * Quick'n'Dirty Bresenham line drawing routine. ***************************************************************************/ #define SIGN(x) ((x<0)?-1:((x>0)?1:0)) void DrawLine (int x1, int y1, int x2, int y2, u8 r, u8 g, u8 b) { u32 colour, pixel; u32 colourhi, colourlo; int i, dx, dy, sdx, sdy, dxabs, dyabs, x, y, px, py; int sp; colour = getcolour (r, g, b); colourhi = colour & 0xffff0000; colourlo = colour & 0xffff; dx = x2 - x1; /*** Horizontal distance ***/ dy = y2 - y1; /*** Vertical distance ***/ dxabs = abs (dx); dyabs = abs (dy); sdx = SIGN (dx); sdy = SIGN (dy); x = dyabs >> 1; y = dxabs >> 1; px = x1; py = y1; sp = (py * 320) + (px >> 1); pixel = xfb[whichfb][sp]; /*** Plot this pixel ***/ if (px & 1) xfb[whichfb][sp] = (pixel & 0xffff0000) | colourlo; else xfb[whichfb][sp] = (pixel & 0xffff) | colourhi; if (dxabs >= dyabs) /*** Mostly horizontal ***/ { for (i = 0; i < dxabs; i++) { y += dyabs; if (y >= dxabs) { y -= dxabs; py += sdy; } px += sdx; sp = (py * 320) + (px >> 1); pixel = xfb[whichfb][sp]; if (px & 1) xfb[whichfb][sp] = (pixel & 0xffff0000) | colourlo; else xfb[whichfb][sp] = (pixel & 0xffff) | colourhi; } } else { for (i = 0; i < dyabs; i++) { x += dxabs; if (x >= dyabs) { x -= dyabs; px += sdx; } py += sdy; sp = (py * 320) + (px >> 1); pixel = xfb[whichfb][sp]; if (px & 1) xfb[whichfb][sp] = (pixel & 0xffff0000) | colourlo; else xfb[whichfb][sp] = (pixel & 0xffff) | colourhi; } } } /**************************************************************************** * Progress Bar * * Show the user what's happening ***************************************************************************/ void ShowProgress (const char *msg, int done, int total) { if(total <= 0) // division by 0 is bad! return; else if(done > total) // this shouldn't happen done = total; else if(total < (256*1024)) // don't bother showing progress for small files return; int xpos, ypos; int i; if(done < 5000) // we just started! { ypos = (screenheight - 30) >> 1; if (screenheight == 480) ypos += 52; else ypos += 32; clearscreen (); setfontsize(20); DrawText (-1, ypos, msg); /*** Draw a white outline box ***/ for (i = 380; i < 401; i++) DrawLine (100, i, 540, i, 0xff, 0xff, 0xff); } /*** Show progess ***/ xpos = (int) (((float) done / (float) total) * 438); for (i = 381; i < 400; i++) DrawLine (101, i, 101 + xpos, i, 0x00, 0x00, 0x80); if(done < 5000) // we just started! { DrawVersion(); showscreen (); } } /**************************************************************************** * DrawPolygon ***************************************************************************/ void DrawPolygon (int vertices, int varray[], u8 r, u8 g, u8 b) { int i; for (i = 0; i < vertices - 1; i++) { DrawLine (varray[(i << 1)], varray[(i << 1) + 1], varray[(i << 1) + 2], varray[(i << 1) + 3], r, g, b); } DrawLine (varray[0], varray[1], varray[(vertices << 1) - 2], varray[(vertices << 1) - 1], r, g, b); } /**************************************************************************** * Draw Line Fast * * This routine requires that start and endx are 32bit aligned. * It tries to perform a semi-transparency over the existing image. ***************************************************************************/ #define SRCWEIGHT 0.7f #define DSTWEIGHT (1.0f - SRCWEIGHT) static inline u8 c_adjust( u8 c , float weight ) { return (u8)((float)c * weight); } void DrawLineFast( int startx, int endx, int y, u8 r, u8 g, u8 b ) { int width; u32 offset; int i; u32 colour, clo, chi; u32 lo,hi; u8 *s, *d; //colour = getcolour(r, g, b); colour = ( r << 16 | g << 8 | b ); d = (u8 *)&colour; d[1] = c_adjust(d[1], DSTWEIGHT); d[2] = c_adjust(d[2], DSTWEIGHT); d[3] = c_adjust(d[3], DSTWEIGHT); width = ( endx - startx ) >> 1; offset = ( y << 8 ) + ( y << 6 ) + ( startx >> 1 ); for ( i = 0; i < width; i++ ) { lo = getrgb(xfb[whichfb][offset], 0); hi = getrgb(xfb[whichfb][offset], 1); s = (u8 *)&hi; s[1] = ( ( c_adjust(s[1],SRCWEIGHT) ) + d[1] ); s[2] = ( ( c_adjust(s[2],SRCWEIGHT) ) + d[2] ); s[3] = ( ( c_adjust(s[3],SRCWEIGHT) ) + d[3] ); s = (u8 *)&lo; s[1] = ( ( c_adjust(s[1],SRCWEIGHT) ) + d[1] ); s[2] = ( ( c_adjust(s[2],SRCWEIGHT) ) + d[2] ); s[3] = ( ( c_adjust(s[3],SRCWEIGHT) ) + d[3] ); clo = getcolour( s[1], s[2], s[3] ); s = (u8 *)&hi; chi = getcolour( s[1], s[2], s[3] ); xfb[whichfb][offset++] = (chi & 0xffff0000 ) | ( clo & 0xffff) ; } } /**************************************************************************** * Ok, I'm useless with Y1CBY2CR colour. * So convert back to RGB so I can work with it -;) ***************************************************************************/ u32 getrgb( u32 ycbr, u32 low ) { u8 r,g,b; u32 y; s8 cb,cr; if ( low ) y = ( ycbr & 0xff00 ) >> 8; else y = ( ycbr & 0xff000000 ) >> 24; cr = ycbr & 0xff; cb = ( ycbr & 0xff0000 ) >> 16; cr -= 128; cb -= 128; r = (u8)((float)y + 1.371 * (float)cr); g = (u8)((float)y - 0.698 * (float)cr - 0.336 * (float)cb); b = (u8)((float)y + 1.732 * (float)cb); return (u32)( r << 16 | g << 8 | b ); }