From a12e01162349cd970e9b6fe5674497760631279b Mon Sep 17 00:00:00 2001 From: notaz Date: Fri, 24 Aug 2007 20:47:28 +0000 Subject: [PATCH] some skinning capabilities git-svn-id: file:///home/notaz/opt/svn/PicoDrive@225 be3aeb3a-fb24-0410-a615-afba39da0efa --- Pico/Pico.c | 7 + Pico/Pico.h | 1 + platform/gp2x/940ctl.c | 8 +- platform/gp2x/Makefile | 5 +- platform/gp2x/asmutils.h | 2 +- platform/gp2x/asmutils.s | 1 - platform/gp2x/emu.c | 78 +++++- platform/gp2x/gp2x.c | 13 +- platform/gp2x/gp2x.h | 1 + platform/gp2x/main.c | 1 + platform/gp2x/menu.c | 555 ++++++++++++++++++++++++--------------- platform/gp2x/menu.h | 7 +- platform/gp2x/readpng.c | 143 ++++++++++ platform/gp2x/readpng.h | 10 + platform/linux/Makefile | 5 +- platform/linux/gp2x.c | 9 +- platform/readme.txt | 2 + skin/background.png | Bin 0 -> 44615 bytes skin/font.png | Bin 0 -> 3185 bytes skin/selector.png | Bin 0 -> 261 bytes skin/skin.txt | 4 + 21 files changed, 603 insertions(+), 249 deletions(-) create mode 100644 platform/gp2x/readpng.c create mode 100644 platform/gp2x/readpng.h create mode 100644 skin/background.png create mode 100644 skin/font.png create mode 100644 skin/selector.png create mode 100644 skin/skin.txt diff --git a/Pico/Pico.c b/Pico/Pico.c index 62b1b48..e6a3b29 100644 --- a/Pico/Pico.c +++ b/Pico/Pico.c @@ -653,6 +653,13 @@ int PicoFrame(void) return 0; } +void PicoFrameDrawOnly(void) +{ + int y; + PicoFrameStart(); + for (y=0;y<224;y++) PicoLine(y); +} + // callback to output message from emu void (*PicoMessage)(const char *msg)=NULL; diff --git a/Pico/Pico.h b/Pico/Pico.h index ec5148b..a646d5b 100644 --- a/Pico/Pico.h +++ b/Pico/Pico.h @@ -41,6 +41,7 @@ int PicoInit(void); void PicoExit(void); int PicoReset(int hard); int PicoFrame(void); +void PicoFrameDrawOnly(void); extern int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU extern void (*PicoWriteSound)(int len); // called once per frame at the best time to send sound buffer (PsndOut) to hardware extern void (*PicoMessage)(const char *msg); // callback to output text message from emu diff --git a/platform/gp2x/940ctl.c b/platform/gp2x/940ctl.c index 54b83b6..66a5d2b 100644 --- a/platform/gp2x/940ctl.c +++ b/platform/gp2x/940ctl.c @@ -415,10 +415,10 @@ void YM2612Init_940(int baseclock, int rate) fp = fopen(binpath, "rb"); if(!fp) { - memset(gp2x_screen, 0, 320*240); - gp2x_text_out8(10, 100, "failed to open required file:"); - gp2x_text_out8(10, 110, CODE940_FILE); - gp2x_video_flip(); + memset(gp2x_screen, 0, 320*240*2); + text_out16(10, 100, "failed to open required file:"); + text_out16(10, 110, CODE940_FILE); + gp2x_video_flip2(); printf("failed to open %s\n", binpath); exit(1); } diff --git a/platform/gp2x/Makefile b/platform/gp2x/Makefile index 489d62f..e9ffd46 100644 --- a/platform/gp2x/Makefile +++ b/platform/gp2x/Makefile @@ -47,7 +47,7 @@ LD = $(CROSS)ld OBJCOPY = $(CROSS)objcopy # frontend -OBJS += main.o menu.o fonts.o gp2x.o usbjoy.o emu.o squidgehack.o asmutils.o cpuctrl.o +OBJS += main.o menu.o fonts.o gp2x.o usbjoy.o emu.o squidgehack.o asmutils.o cpuctrl.o readpng.o # 940 core control OBJS += 940ctl.o # Pico @@ -121,7 +121,7 @@ all: PicoDrive.gpe PicoDrive.gpe : $(OBJS) helix/helix_mp3.a @echo $@ - @$(GCC) -o $@ $(COPT) $^ -lm -Wl,-Map=PicoDrive.map + @$(GCC) -o $@ $(COPT) $^ -lm -lpng -Wl,-Map=PicoDrive.map ifeq ($(DEBUG),) @$(STRIP) $@ endif @@ -212,6 +212,7 @@ endif rel: PicoDrive.gpe code940/pico940.bin ../readme.txt config.txt PicoDrive.man.txt PicoDrive.png zip -9 -j ../../PicoDrive_$(VER).zip $^ mmuhack.o + zip -9 PicoDrive_$(VER).zip skin code940/code940.bin: make -C code940/ diff --git a/platform/gp2x/asmutils.h b/platform/gp2x/asmutils.h index cade08c..902064a 100644 --- a/platform/gp2x/asmutils.h +++ b/platform/gp2x/asmutils.h @@ -10,5 +10,5 @@ void vidCpyM2_40col(void *dest, void *src); void vidCpyM2_32col(void *dest, void *src); void vidCpyM2_32col_nobord(void *dest, void *src); void spend_cycles(int c); // utility -void flushcache(void); +void flushcache(void *beginning_addr, void *end_addr, unsigned int flags); diff --git a/platform/gp2x/asmutils.s b/platform/gp2x/asmutils.s index e4402ff..787ecb9 100644 --- a/platform/gp2x/asmutils.s +++ b/platform/gp2x/asmutils.s @@ -211,7 +211,6 @@ spend_cycles: bx lr -@ test .global flushcache flushcache: diff --git a/platform/gp2x/emu.c b/platform/gp2x/emu.c index bfbc0c1..939ba93 100644 --- a/platform/gp2x/emu.c +++ b/platform/gp2x/emu.c @@ -20,6 +20,7 @@ #include "menu.h" #include "asmutils.h" #include "cpuctrl.h" +#include "fonts.h" #include #include @@ -634,6 +635,53 @@ void emu_Deinit(void) set_gamma(100, 0); } +static void text_out8_builtin(int x, int y, const char *text) +{ + int i,l,len=strlen(text); + unsigned char *screen = (unsigned char *)gp2x_screen + x + y*320; + + /* always using built-in font */ + for (i = 0; i < len; i++) + { + for (l=0;l<8;l++) + { + unsigned char fd = fontdata8x8[((text[i])*8)+l]; + if (fd&0x80) screen[l*320+0]=0xf0; + if (fd&0x40) screen[l*320+1]=0xf0; + if (fd&0x20) screen[l*320+2]=0xf0; + if (fd&0x10) screen[l*320+3]=0xf0; + if (fd&0x08) screen[l*320+4]=0xf0; + if (fd&0x04) screen[l*320+5]=0xf0; + if (fd&0x02) screen[l*320+6]=0xf0; + if (fd&0x01) screen[l*320+7]=0xf0; + } + screen += 8; + } +} + +static void text_out16_builtin(int x, int y, const char *text) +{ + int i,l,len=strlen(text); + unsigned short *screen = (unsigned short *)gp2x_screen + x + y*320; + + for (i = 0; i < len; i++) + { + for (l=0;l<8;l++) + { + unsigned char fd = fontdata8x8[((text[i])*8)+l]; + if(fd&0x80) screen[l*320+0]=0xffff; + if(fd&0x40) screen[l*320+1]=0xffff; + if(fd&0x20) screen[l*320+2]=0xffff; + if(fd&0x10) screen[l*320+3]=0xffff; + if(fd&0x08) screen[l*320+4]=0xffff; + if(fd&0x04) screen[l*320+5]=0xffff; + if(fd&0x02) screen[l*320+6]=0xffff; + if(fd&0x01) screen[l*320+7]=0xffff; + } + screen += 8; + } +} + void osd_text(int x, int y, const char *text) { @@ -647,7 +695,7 @@ void osd_text(int x, int y, const char *text) p = (int *) ((unsigned char *) gp2x_screen+x+320*(y+h)); for (i = len; i; i--, p++) *p = 0xe0e0e0e0; } - gp2x_text_out8_2(x, y, text, 0xf0); + text_out8_builtin(x, y, text); } else { int *p, i, h; x &= ~1; // align x @@ -656,7 +704,7 @@ void osd_text(int x, int y, const char *text) p = (int *) ((unsigned short *) gp2x_screen+x+320*(y+h)); for (i = len; i; i--, p++) *p = (*p>>2)&0x39e7; } - gp2x_text_out15(x, y, text); + text_out16_builtin(x, y, text); } } @@ -794,7 +842,7 @@ static void vidResetMode(void) if (PicoOpt&0x10) { gp2x_video_changemode(8); } else if (currentConfig.EmuOpt&0x80) { - gp2x_video_changemode(15); + gp2x_video_changemode(16); PicoDrawSetColorFormat(1); PicoScan = EmuScan16; PicoScan(0, 0); @@ -1045,10 +1093,20 @@ static void SkipFrame(int do_audio) void emu_forced_frame(void) { int po_old = PicoOpt; + int eo_old = currentConfig.EmuOpt; + + PicoOpt &= ~0x0010; + PicoOpt |= 0x4080; // soft_scale | acc_sprites + currentConfig.EmuOpt |= 0x80; - PicoOpt |= 0x10; - PicoFrameFull(); + //vidResetMode(); + PicoDrawSetColorFormat(1); + PicoScan = EmuScan16; + PicoScan(0, 0); + Pico.m.dirtyPal = 1; + PicoFrameDrawOnly(); +/* if (!(Pico.video.reg[12]&1)) { vidCpyM2 = vidCpyM2_32col; clearArea(1); @@ -1057,8 +1115,9 @@ void emu_forced_frame(void) vidCpyM2((unsigned char *)gp2x_screen+320*8, framebuff+328*8); vidConvCpyRGB32(localPal, Pico.cram, 0x40); gp2x_video_setpalette(localPal, 0x40); - +*/ PicoOpt = po_old; + currentConfig.EmuOpt = eo_old; } static void simpleWait(int thissec, int lim_time) @@ -1375,12 +1434,9 @@ if (Pico.m.frame_count == 31563) { SRam.changed = 0; } - // if in 16bit mode, generate 8it image for menu background - if (!(PicoOpt&0x10) && (currentConfig.EmuOpt&0x80)) + // if in 8bit mode, generate 16bit image for menu background + if ((PicoOpt&0x10) || !(currentConfig.EmuOpt&0x80)) emu_forced_frame(); - - // for menu bg - gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2); } diff --git a/platform/gp2x/gp2x.c b/platform/gp2x/gp2x.c index c0396d9..3b5d125 100644 --- a/platform/gp2x/gp2x.c +++ b/platform/gp2x/gp2x.c @@ -157,6 +157,15 @@ void gp2x_video_wait_vsync(void) } +void gp2x_video_flush_cache(void) +{ + // since we are using the mmu hack, we must flush the cache first + // (the params are most likely wrong, but they seem to work somehow) + //flushcache(addr, addr + 320*240*2, 0); + flushcache(gp2x_screen, (char *)gp2x_screen + 320*240*2, 0); +} + + void gp2x_memcpy_buffers(int buffers, void *data, int offset, int len) { char *dst; @@ -184,9 +193,7 @@ void gp2x_memset_all_buffers(int offset, int byte, int len) void gp2x_pd_clone_buffer2(void) { - memcpy(gp2x_screen, gp2x_screens[2], 320*240); - memset(gp2x_screen, 0xe0, 320*8); - memset(gp2x_screen + 320*232, 0xe0, 320*8); + memcpy(gp2x_screen, gp2x_screens[2], 320*240*2); } diff --git a/platform/gp2x/gp2x.h b/platform/gp2x/gp2x.h index 349b898..2d2e535 100644 --- a/platform/gp2x/gp2x.h +++ b/platform/gp2x/gp2x.h @@ -14,6 +14,7 @@ void gp2x_video_changemode2(int bpp); void gp2x_video_setpalette(int *pal, int len); void gp2x_video_RGB_setscaling(int ln_offs, int W, int H); void gp2x_video_wait_vsync(void); +void gp2x_video_flush_cache(void); void gp2x_memcpy_buffers(int buffers, void *data, int offset, int len); void gp2x_memcpy_all_buffers(void *data, int offset, int len); void gp2x_memset_all_buffers(int offset, int byte, int len); diff --git a/platform/gp2x/main.c b/platform/gp2x/main.c index d6438f6..e4e3bcf 100644 --- a/platform/gp2x/main.c +++ b/platform/gp2x/main.c @@ -97,6 +97,7 @@ int main(int argc, char *argv[]) } sharedmem_init(); emu_Init(); + menu_init(); engineState = PGS_Menu; diff --git a/platform/gp2x/menu.c b/platform/gp2x/menu.c index 06a3e3d..b45b295 100644 --- a/platform/gp2x/menu.c +++ b/platform/gp2x/menu.c @@ -16,6 +16,7 @@ #include "fonts.h" #include "usbjoy.h" #include "asmutils.h" +#include "readpng.h" #include "version.h" #include @@ -32,66 +33,71 @@ extern int mmuhack_status; extern int state_slot; extern int config_slot, config_slot_current; +static unsigned char menu_font_data[10240]; static char *gp2xKeyNames[] = { "UP", "???", "LEFT", "???", "DOWN", "???", "RIGHT", "???", "START", "SELECT", "L", "R", "A", "B", "X", "Y", "???", "???", "???", "???", "???", "???", "VOL DOWN", "VOL UP", "???", "???", "???", "PUSH", "???", "???", "???", "???" }; +static int menu_text_color = 0xffff; // default to white +static int menu_sel_color = -1; // disabled char menuErrorMsg[40] = {0, }; +static void menu_darken_bg(void *dst, int pixels); +static void menu_prepare_bg(int use_game_bg); -static void gp2x_text(unsigned char *screen, int x, int y, const char *text, int color) +// draws text to current bbp16 screen +static void text_out16_(int x, int y, const char *text, int color) { - int i,l; + int i, l, u, tr, tg, tb, len; + unsigned short *dest = (unsigned short *)gp2x_screen + x + y*320; + tr = (color & 0xf800) >> 8; + tg = (color & 0x07e0) >> 3; + tb = (color & 0x001f) << 3; - screen = screen + x + y*320; - - for (i = 0; i < strlen(text); i++) + if (text == (void *)1) { - for (l=0;l<8;l++) - { - if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=color; - if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=color; - if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=color; - if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=color; - if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=color; - if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=color; - if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=color; - if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=color; - } - screen += 8; + // selector symbol + text = ""; + len = 1; } -} - -// draws white text to current bbp15 screen -void gp2x_text_out15(int x, int y, const char *text) -{ - int i,l; - unsigned short *screen = gp2x_screen; - - screen = screen + x + y*320; + else + len = strlen(text); - for (i = 0; i < strlen(text); i++) + for (i = 0; i < len; i++) { - for (l=0;l<8;l++) + unsigned char *src = menu_font_data + (unsigned int)text[i]*4*10; + unsigned short *dst = dest; + for (l = 0; l < 10; l++, dst += 320-8) { - if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=0xffff; - if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=0xffff; - if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=0xffff; - if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=0xffff; - if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=0xffff; - if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=0xffff; - if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=0xffff; - if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=0xffff; + for (u = 8/2; u > 0; u--, src++) + { + int c, r, g, b; + c = *src >> 4; + r = (*dst & 0xf800) >> 8; + g = (*dst & 0x07e0) >> 3; + b = (*dst & 0x001f) << 3; + r = (c^0xf)*r/15 + c*tr/15; + g = (c^0xf)*g/15 + c*tg/15; + b = (c^0xf)*b/15 + c*tb/15; + *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3); + c = *src & 0xf; + r = (*dst & 0xf800) >> 8; + g = (*dst & 0x07e0) >> 3; + b = (*dst & 0x001f) << 3; + r = (c^0xf)*r/15 + c*tr/15; + g = (c^0xf)*g/15 + c*tg/15; + b = (c^0xf)*b/15 + c*tb/15; + *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3); + } } - screen += 8; + dest += 8; } } - -void gp2x_text_out8(int x, int y, const char *texto, ...) +void text_out16(int x, int y, const char *texto, ...) { va_list args; char buffer[512]; @@ -100,31 +106,15 @@ void gp2x_text_out8(int x, int y, const char *texto, ...) vsprintf(buffer,texto,args); va_end(args); - gp2x_text(gp2x_screen,x,y,buffer,0xf0); -} - - -void gp2x_text_out8_2(int x, int y, const char *texto, int color) -{ - gp2x_text(gp2x_screen, x, y, texto, color); + text_out16_(x,y,buffer,menu_text_color); } -void gp2x_text_out8_lim(int x, int y, const char *texto, int max) -{ - char buffer[320/8+1]; - - strncpy(buffer, texto, 320/8); - if (max > 320/8) max = 320/8; - if (max < 0) max = 0; - buffer[max] = 0; - gp2x_text(gp2x_screen,x,y,buffer,0xf0); -} - -static void gp2x_smalltext8(int x, int y, const char *texto) +static void smalltext_out16(int x, int y, const char *texto, int color) { int i; - unsigned char *src, *dst; + unsigned char *src; + unsigned short *dst; for (i = 0;; i++, x += 6) { @@ -134,14 +124,14 @@ static void gp2x_smalltext8(int x, int y, const char *texto) if (!c) break; src = fontdata6x8[c]; - dst = (unsigned char *)gp2x_screen + x + y*320; + dst = (unsigned short *)gp2x_screen + x + y*320; while (h--) { int w = 0x20; while (w) { - if( *src & w ) *dst = 0xf0; + if( *src & w ) *dst = color; dst++; w>>=1; } @@ -152,7 +142,7 @@ static void gp2x_smalltext8(int x, int y, const char *texto) } } -static void gp2x_smalltext8_lim(int x, int y, const char *texto, int max) +static void smalltext_out16_lim(int x, int y, const char *texto, int color, int max) { char buffer[320/6+1]; @@ -161,7 +151,33 @@ static void gp2x_smalltext8_lim(int x, int y, const char *texto, int max) if (max < 0) max = 0; buffer[max] = 0; - gp2x_smalltext8(x, y, buffer); + smalltext_out16(x, y, buffer, color); +} + +static void draw_selection(int x, int y, int w) +{ + int i, h; + unsigned short *dst, *dest; + + text_out16_(x, y, (void *)1, (menu_sel_color < 0) ? menu_text_color : menu_sel_color); + + if (menu_sel_color < 0) return; // no selection hilight + + if (y > 0) y--; + dest = (unsigned short *)gp2x_screen + x + y*320 + 14; + for (h = 11; h > 0; h--) + { + dst = dest; + for (i = w; i > 0; i--) + *dst++ = menu_sel_color; + dest += 320; + } +} + +static void menu_flip(void) +{ + gp2x_video_flush_cache(); + gp2x_video_flip2(); } @@ -303,11 +319,11 @@ static void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_ y1 += 10; continue; } - gp2x_text_out8(x, y1, entries[i].name); + text_out16(x, y1, entries[i].name); if (entries[i].beh == MB_ONOFF) - gp2x_text_out8(x + 27*8, y1, (*(int *)entries[i].var & entries[i].mask) ? "ON" : "OFF"); + text_out16(x + 27*8, y1, (*(int *)entries[i].var & entries[i].mask) ? "ON" : "OFF"); else if (entries[i].beh == MB_RANGE) - gp2x_text_out8(x + 27*8, y1, "%i", *(int *)entries[i].var); + text_out16(x + 27*8, y1, "%i", *(int *)entries[i].var); y1 += 10; } } @@ -332,7 +348,73 @@ static int me_process(menu_entry *entries, int count, menu_id id, int is_next) } +static int parse_hex_color(char *buff) +{ + char *endp = buff; + int t = (int) strtoul(buff, &endp, 16); + if (endp != buff) return ((t>>8)&0xf800) | ((t>>5)&0x07e0) | ((t>>3)&0x1f); + return -1; +} + +void menu_init(void) +{ + int c, l; + unsigned char *fd = menu_font_data; + char buff[256]; + FILE *f; + + // generate default font from fontdata8x8 + memset(menu_font_data, 0, sizeof(menu_font_data)); + for (c = 0; c < 256; c++) + { + for (l = 0; l < 8; l++) + { + unsigned char fd8x8 = fontdata8x8[c*8+l]; + if (fd8x8&0x80) *fd |= 0xf0; + if (fd8x8&0x40) *fd |= 0x0f; fd++; + if (fd8x8&0x20) *fd |= 0xf0; + if (fd8x8&0x10) *fd |= 0x0f; fd++; + if (fd8x8&0x08) *fd |= 0xf0; + if (fd8x8&0x04) *fd |= 0x0f; fd++; + if (fd8x8&0x02) *fd |= 0xf0; + if (fd8x8&0x01) *fd |= 0x0f; fd++; + } + fd += 8*2/2; // 2 empty lines + } + + // load custom font and selector (stored as 1st symbol in font table) + readpng(menu_font_data, "skin/font.png", READPNG_FONT); + memcpy(menu_font_data, menu_font_data + ((int)'>')*4*10, 4*10); // default selector symbol is '>' + readpng(menu_font_data, "skin/selector.png", READPNG_SELECTOR); + // load custom colors + f = fopen("skin/skin.txt", "r"); + if (f != NULL) + { + printf("found skin.txt\n"); + while (!feof(f)) + { + fgets(buff, sizeof(buff), f); + if (buff[0] == '#' || buff[0] == '/') continue; // comment + if (buff[0] == '\r' || buff[0] == '\n') continue; // empty line + if (strncmp(buff, "text_color=", 11) == 0) + { + int tmp = parse_hex_color(buff+11); + if (tmp >= 0) menu_text_color = tmp; + else printf("skin.txt: parse error for text_color\n"); + } + else if (strncmp(buff, "selection_color=", 16) == 0) + { + int tmp = parse_hex_color(buff+16); + if (tmp >= 0) menu_sel_color = tmp; + else printf("skin.txt: parse error for selection_color\n"); + } + else + printf("skin.txt: parse error: %s\n", buff); + } + fclose(f); + } +} static unsigned long inp_prev = 0; static int inp_prevjoy = 0; @@ -425,12 +507,12 @@ static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy) static void load_progress_cb(int percent) { int ln, len = percent * 320 / 100; - unsigned char *dst = (unsigned char *)gp2x_screen + 320*20; + unsigned short *dst = (unsigned short *)gp2x_screen + 320*20; if (len > 320) len = 320; for (ln = 10; ln > 0; ln--, dst += 320) - memset(dst, 0xf0, len); - gp2x_video_flip2(); + memset(dst, 0xff, len*2); + menu_flip(); } void menu_romload_prepare(const char *rom_name) @@ -438,23 +520,41 @@ void menu_romload_prepare(const char *rom_name) const char *p = rom_name + strlen(rom_name); while (p > rom_name && *p != '/') p--; - gp2x_pd_clone_buffer2(); - gp2x_smalltext8(1, 1, "Loading"); - gp2x_smalltext8_lim(1, 10, p, 53); - gp2x_memcpy_buffers(3, gp2x_screen, 0, 320*240); - gp2x_video_flip2(); + if (rom_data) gp2x_pd_clone_buffer2(); + else memset(gp2x_screen, 0, 320*240*2); + + smalltext_out16(1, 1, "Loading", 0xffff); + smalltext_out16_lim(1, 10, p, 0xffff, 53); + gp2x_memcpy_buffers(3, gp2x_screen, 0, 320*240*2); + menu_flip(); PicoCartLoadProgressCB = load_progress_cb; } void menu_romload_end(void) { PicoCartLoadProgressCB = NULL; - gp2x_smalltext8(1, 30, "Starting emulation..."); - gp2x_video_flip2(); + smalltext_out16(1, 30, "Starting emulation...", 0xffff); + menu_flip(); } // -------------- ROM selector -------------- +// rrrr rggg gggb bbbb +static unsigned short file2color(const char *fname) +{ + const char *ext = fname + strlen(fname) - 3; + static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso" }; + static const char *other_exts[] = { "gmv", "pat" }; + int i; + + if (ext < fname) ext = fname; + for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++) + if (strcasecmp(ext, rom_exts[i]) == 0) return 0xbdff; + for (i = 0; i < sizeof(other_exts)/sizeof(other_exts[0]); i++) + if (strcasecmp(ext, other_exts[i]) == 0) return 0xaff5; + return 0xffff; +} + static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel) { int start, i, pos; @@ -462,23 +562,31 @@ static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel) start = 12 - sel; n--; // exclude current dir (".") - gp2x_pd_clone_buffer2(); + if (rom_data) + gp2x_pd_clone_buffer2(); + else { + memset(gp2x_screen, 0, 320*240*2); + memset((char *)gp2x_screen + 320*120*2, 0xff, 320*8*2); + } + + menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8); if(start - 2 >= 0) - gp2x_smalltext8_lim(14, (start - 2)*10, curdir, 53-2); + smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2); for (i = 0; i < n; i++) { pos = start + i; if (pos < 0) continue; if (pos > 23) break; if (namelist[i+1]->d_type == DT_DIR) { - gp2x_smalltext8_lim(14, pos*10, "/", 1); - gp2x_smalltext8_lim(14+6, pos*10, namelist[i+1]->d_name, 53-3); + smalltext_out16_lim(14, pos*10, "/", 0xfff6, 1); + smalltext_out16_lim(14+6, pos*10, namelist[i+1]->d_name, 0xfff6, 53-3); } else { - gp2x_smalltext8_lim(14, pos*10, namelist[i+1]->d_name, 53-2); + unsigned short color = file2color(namelist[i+1]->d_name); + smalltext_out16_lim(14, pos*10, namelist[i+1]->d_name, color, 53-2); } } - gp2x_text_out8(5, 120, ">"); - gp2x_video_flip2(); + text_out16(5, 120, ">"); + menu_flip(); } static int scandir_cmp(const void *p1, const void *p2) @@ -637,11 +745,11 @@ static void draw_debug(void) while (*p && *p != '\n') p++; len = p - str; if (len > 55) len = 55; - gp2x_smalltext8_lim(1, line*10, str, len); + smalltext_out16_lim(1, line*10, str, 0xffff, len); if (*p == 0) break; p++; str = p; } - gp2x_video_flip2(); + menu_flip(); } static void debug_menu_loop(void) @@ -654,7 +762,7 @@ static void debug_menu_loop(void) static void draw_patchlist(int sel) { - int start, i, pos; + int start, i, pos, active; start = 12 - sel; @@ -664,14 +772,15 @@ static void draw_patchlist(int sel) pos = start + i; if (pos < 0) continue; if (pos > 23) break; - gp2x_smalltext8_lim(14, pos*10, PicoPatches[i].active ? "ON " : "OFF", 3); - gp2x_smalltext8_lim(14+6*4, pos*10, PicoPatches[i].name, 53-6); + active = PicoPatches[i].active; + smalltext_out16_lim(14, pos*10, active ? "ON " : "OFF", active ? 0xfff6 : 0xffff, 3); + smalltext_out16_lim(14+6*4, pos*10, PicoPatches[i].name, active ? 0xfff6 : 0xffff, 53-6); } pos = start + i; - if (pos < 24) gp2x_smalltext8_lim(14, pos*10, "done", 4); + if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4); - gp2x_text_out8(5, 120, ">"); - gp2x_video_flip2(); + text_out16(5, 120, ">"); + menu_flip(); } @@ -700,8 +809,6 @@ static void patches_menu_loop(void) // ------------ savestate loader ------------ -static void menu_prepare_bg(void); - static int state_slot_flags = 0; static void state_check_slots(void) @@ -762,8 +869,7 @@ static void draw_savestate_bg(int slot) } emu_forced_frame(); - gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2); - menu_prepare_bg(); + menu_prepare_bg(1); memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram)); memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram)); @@ -780,20 +886,19 @@ static void draw_savestate_menu(int menu_sel, int is_loading) draw_savestate_bg(menu_sel); gp2x_pd_clone_buffer2(); - gp2x_text_out8(tl_x, 30, is_loading ? "Load state" : "Save state"); + text_out16(tl_x, 30, is_loading ? "Load state" : "Save state"); + + draw_selection(tl_x - 16, tl_y + menu_sel*10, 108); /* draw all 10 slots */ y = tl_y; for (i = 0; i < 10; i++, y+=10) { - gp2x_text_out8(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free"); + text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free"); } - gp2x_text_out8(tl_x, y, "back"); + text_out16(tl_x, y, "back"); - // draw cursor - gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); - - gp2x_video_flip2(); + menu_flip(); } static int savestate_menu_loop(int is_loading) @@ -918,33 +1023,32 @@ static void draw_key_config(const bind_action_t *opts, int opt_cnt, int player_i gp2x_pd_clone_buffer2(); if (player_idx >= 0) { - gp2x_text_out8(80, 20, "Player %i controls", player_idx + 1); + text_out16(80, 20, "Player %i controls", player_idx + 1); x = 100; } else { - gp2x_text_out8(80, 20, "Emulator controls"); + text_out16(80, 20, "Emulator controls"); x = 40; } + draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130); + y = tl_y; for (i = 0; i < opt_cnt; i++, y+=10) - gp2x_text_out8(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask)); + text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask)); - gp2x_text_out8(x, y, "Done"); - - // draw cursor - gp2x_text_out8(x - 16, tl_y + sel*10, ">"); + text_out16(x, y, "Done"); if (sel < opt_cnt) { - gp2x_text_out8(30, 190, "Press a button to bind/unbind"); - gp2x_text_out8(30, 200, "Use SELECT to clear"); - gp2x_text_out8(30, 210, "To bind UP/DOWN, hold SELECT"); - gp2x_text_out8(30, 220, "Select \"Done\" to exit"); + text_out16(30, 180, "Press a button to bind/unbind"); + text_out16(30, 190, "Use SELECT to clear"); + text_out16(30, 200, "To bind UP/DOWN, hold SELECT"); + text_out16(30, 210, "Select \"Done\" to exit"); } else { - gp2x_text_out8(30, 200, "Use Options -> Save cfg"); - gp2x_text_out8(30, 210, "to save controls"); - gp2x_text_out8(30, 220, "Press B or X to exit"); + text_out16(30, 190, "Use Options -> Save cfg"); + text_out16(30, 200, "to save controls"); + text_out16(30, 210, "Press B or X to exit"); } - gp2x_video_flip2(); + menu_flip(); } static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx) @@ -1007,26 +1111,25 @@ static void draw_kc_sel(int menu_sel) y = tl_y; gp2x_pd_clone_buffer2(); - gp2x_text_out8(tl_x, y, "Player 1"); - gp2x_text_out8(tl_x, (y+=10), "Player 2"); - gp2x_text_out8(tl_x, (y+=10), "Emulator controls"); - gp2x_text_out8(tl_x, (y+=10), "Done"); + draw_selection(tl_x - 16, tl_y + menu_sel*10, 138); - // draw cursor - gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + text_out16(tl_x, y, "Player 1"); + text_out16(tl_x, (y+=10), "Player 2"); + text_out16(tl_x, (y+=10), "Emulator controls"); + text_out16(tl_x, (y+=10), "Done"); tl_x = 25; - gp2x_text_out8(tl_x, (y=110), "USB joys detected:"); + text_out16(tl_x, (y=110), "USB joys detected:"); if (num_of_joys > 0) { for (i = 0; i < num_of_joys; i++) { strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0; - gp2x_text_out8(tl_x, (y+=10), "%i: %s", i+1, joyname); + text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname); } } else { - gp2x_text_out8(tl_x, (y+=10), "none"); + text_out16(tl_x, (y+=10), "none"); } - gp2x_video_flip2(); + menu_flip(); } @@ -1052,13 +1155,13 @@ static bind_action_t ctrl_actions[] = // "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE" static bind_action_t emuctrl_actions[] = { - { "Volume Down ", 1<<30 }, - { "Volume Up ", 1<<29 }, { "Load State ", 1<<28 }, { "Save State ", 1<<27 }, - { "Switch Renderer", 1<<26 }, { "Prev Save Slot ", 1<<25 }, { "Next Save Slot ", 1<<24 }, + { "Switch Renderer", 1<<26 }, + { "Volume Down ", 1<<30 }, + { "Volume Up ", 1<<29 }, { "Enter Menu ", 1<<23 }, }; @@ -1121,13 +1224,13 @@ static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *pa switch (entry->id) { - case MA_CDOPT_TESTBIOS_USA: gp2x_text_out8(x, y, "USA BIOS: %s", bios_names->us); break; - case MA_CDOPT_TESTBIOS_EUR: gp2x_text_out8(x, y, "EUR BIOS: %s", bios_names->eu); break; - case MA_CDOPT_TESTBIOS_JAP: gp2x_text_out8(x, y, "JAP BIOS: %s", bios_names->jp); break; + case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break; + case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break; + case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break; case MA_CDOPT_READAHEAD: if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2); else strcpy(ra_buff, " OFF"); - gp2x_text_out8(x, y, "ReadAhead buffer %s", ra_buff); + text_out16(x, y, "ReadAhead buffer %s", ra_buff); break; default:break; } @@ -1144,18 +1247,17 @@ static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names) gp2x_pd_clone_buffer2(); - me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names); + draw_selection(tl_x - 16, tl_y + menu_sel*10, 246); - // draw cursor - gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names); selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel); if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) || (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) || (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND"))) - gp2x_text_out8(tl_x, 220, "Press start to test selected BIOS"); + text_out16(tl_x, 210, "Press start to test selected BIOS"); - gp2x_video_flip2(); + menu_flip(); } static void cd_menu_loop_options(void) @@ -1263,9 +1365,9 @@ menu_entry opt2_entries[] = static void menu_opt2_cust_draw(const menu_entry *entry, int x, int y, void *param) { if (entry->id == MA_OPT2_GAMMA) - gp2x_text_out8(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100); + text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100); else if (entry->id == MA_OPT2_SQUIDGEHACK) - gp2x_text_out8(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)", + text_out16(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)", (currentConfig.EmuOpt&0x0010)?"ON":"OFF"); } @@ -1276,12 +1378,11 @@ static void draw_amenu_options(int menu_sel) gp2x_pd_clone_buffer2(); - me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL); + draw_selection(tl_x - 16, tl_y + menu_sel*10, 252); - // draw cursor - gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL); - gp2x_video_flip2(); + menu_flip(); } static void amenu_loop_options(void) @@ -1388,7 +1489,7 @@ static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *para str = "16bit accurate"; else str = " 8bit accurate"; - gp2x_text_out8(x, y, "Renderer: %s", str); + text_out16(x, y, "Renderer: %s", str); break; case MA_OPT_SCALING: switch (currentConfig.scaling) { @@ -1397,20 +1498,20 @@ static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *para case 2: str = "hw horiz. + vert."; break; case 3: str = "sw horizontal"; break; } - gp2x_text_out8(x, y, "Scaling: %s", str); + text_out16(x, y, "Scaling: %s", str); break; case MA_OPT_FRAMESKIP: if (currentConfig.Frameskip < 0) strcpy(str24, "Auto"); else sprintf(str24, "%i", currentConfig.Frameskip); - gp2x_text_out8(x, y, "Frameskip %s", str24); + text_out16(x, y, "Frameskip %s", str24); break; case MA_OPT_SOUND_QUALITY: str = (currentConfig.PicoOpt&0x08)?"stereo":"mono"; - gp2x_text_out8(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str); + text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str); break; case MA_OPT_REGION: - gp2x_text_out8(x, y, "Region: %s", region_name(currentConfig.PicoRegion)); + text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion)); break; case MA_OPT_CONFIRM_STATES: switch ((currentConfig.EmuOpt >> 9) & 5) { @@ -1419,18 +1520,18 @@ static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *para case 4: str = "loads"; break; case 5: str = "both"; break; } - gp2x_text_out8(x, y, "Confirm savestate %s", str); + text_out16(x, y, "Confirm savestate %s", str); break; case MA_OPT_CPU_CLOCKS: - gp2x_text_out8(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock); + text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock); break; case MA_OPT_SAVECFG: str24[0] = 0; if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot); - gp2x_text_out8(x, y, "Save cfg as default%s", str24); + text_out16(x, y, "Save cfg as default%s", str24); break; case MA_OPT_LOADCFG: - gp2x_text_out8(x, y, "Load cfg from profile %i", config_slot); + text_out16(x, y, "Load cfg from profile %i", config_slot); break; default: printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id); @@ -1442,16 +1543,15 @@ static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *para static void draw_menu_options(int menu_sel) { - int tl_x = 25, tl_y = 32; + int tl_x = 25, tl_y = 24; gp2x_pd_clone_buffer2(); - me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL); + draw_selection(tl_x - 16, tl_y + menu_sel*10, 284); - // draw cursor - gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL); - gp2x_video_flip2(); + menu_flip(); } static int sndrate_prevnext(int rate, int dir) @@ -1635,28 +1735,28 @@ static int menu_loop_options(void) static void draw_menu_credits(void) { - int tl_x = 15, tl_y = 70, y; - //memset(gp2x_screen, 0, 320*240); + int tl_x = 15, tl_y = 64, y; gp2x_pd_clone_buffer2(); - gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007"); + text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007"); y = tl_y; - gp2x_text_out8(tl_x, y, "Credits:"); - gp2x_text_out8(tl_x, (y+=10), "Dave: Cyclone 68000 core,"); - gp2x_text_out8(tl_x, (y+=10), " base code of PicoDrive"); - gp2x_text_out8(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core"); - gp2x_text_out8(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores"); - gp2x_text_out8(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs"); - gp2x_text_out8(tl_x, (y+=10), "Stephane Dallongeville:"); - gp2x_text_out8(tl_x, (y+=10), " opensource Gens"); - gp2x_text_out8(tl_x, (y+=10), "Haze: Genesis hw info"); - gp2x_text_out8(tl_x, (y+=10), "rlyeh and others: minimal SDK"); - gp2x_text_out8(tl_x, (y+=10), "Squidge: squidgehack"); - gp2x_text_out8(tl_x, (y+=10), "Dzz: ARM940 sample"); - gp2x_text_out8(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick"); - gp2x_text_out8(tl_x, (y+=10), "craigix: GP2X hardware"); - - gp2x_video_flip2(); + text_out16(tl_x, y, "Credits:"); + text_out16(tl_x, (y+=10), "Dave: Cyclone 68000 core,"); + text_out16(tl_x, (y+=10), " base code of PicoDrive"); + text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core"); + text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores"); + text_out16(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs"); + text_out16(tl_x, (y+=10), "Stephane Dallongeville:"); + text_out16(tl_x, (y+=10), " opensource Gens"); + text_out16(tl_x, (y+=10), "Haze: Genesis hw info"); + text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK"); + text_out16(tl_x, (y+=10), "Squidge: squidgehack"); + text_out16(tl_x, (y+=10), "Dzz: ARM940 sample"); + text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick"); + text_out16(tl_x, (y+=10), "craigix: GP2X hardware"); + text_out16(tl_x, (y+=10), "ketch: skin design"); + + menu_flip(); } @@ -1684,15 +1784,18 @@ static void draw_menu_root(int menu_sel) gp2x_pd_clone_buffer2(); - gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION); + text_out16(tl_x, 20, "PicoDrive v" VERSION); + + draw_selection(tl_x - 16, tl_y + menu_sel*10, 146); me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL); - // draw cursor - gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); // error - if (menuErrorMsg[0]) gp2x_text_out8(5, 226, menuErrorMsg); - gp2x_video_flip2(); + if (menuErrorMsg[0]) { + memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2); + text_out16(5, 226, menuErrorMsg); + } + menu_flip(); } @@ -1701,18 +1804,6 @@ static void menu_loop_root(void) static int menu_sel = 0; int ret, menu_sel_max; unsigned long inp = 0; - char curr_path[PATH_MAX], *selfname; - FILE *tstf; - - if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) ) - { - fclose(tstf); - strcpy(curr_path, currentConfig.lastRomFile); - } - else - { - getcwd(curr_path, PATH_MAX); - } me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_data != NULL); me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_data != NULL); @@ -1775,12 +1866,24 @@ static void menu_loop_root(void) } break; case MA_MAIN_LOAD_ROM: + { + char curr_path[PATH_MAX], *selfname; + FILE *tstf; + if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) ) + { + fclose(tstf); + strcpy(curr_path, currentConfig.lastRomFile); + } + else + getcwd(curr_path, PATH_MAX); selfname = romsel_loop(curr_path); if (selfname) { printf("selected file: %s\n", selfname); engineState = PGS_ReloadRom; + return; } - return; + break; + } case MA_MAIN_OPTIONS: ret = menu_loop_options(); if (ret == 1) continue; // status update @@ -1815,31 +1918,45 @@ static void menu_loop_root(void) } } - -static void menu_prepare_bg(void) +static void menu_darken_bg(void *dst, int pixels) { - extern int localPal[0x100]; - int c, i; + unsigned int *screen = dst; + pixels /= 2; + while (pixels--) + { + unsigned int p = *screen; + *screen = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3); + screen++; + } +} - // don't clear old palette just for fun (but make it dark) - for (i = 0x100-1; i >= 0; i--) { - c = localPal[i]; - localPal[i] = ((c >> 1) & 0x007f7f7f) - ((c >> 3) & 0x001f1f1f); +static void menu_prepare_bg(int use_game_bg) +{ + if (use_game_bg) + { + // darken the active framebuffer + memset(gp2x_screen, 0, 320*8*2); + menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224); + memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2); + } + else + { + // should really only happen once, on startup.. + readpng(gp2x_screen, "skin/background.png", READPNG_BG); } - localPal[0xe0] = 0x00000000; // reserved pixels for OSD - localPal[0xf0] = 0x00ffffff; - gp2x_video_setpalette(localPal, 0x100); + // copy to buffer2 + gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2); } static void menu_gfx_prepare(void) { - menu_prepare_bg(); + menu_prepare_bg(rom_data != NULL); - // switch to 8bpp - gp2x_video_changemode2(8); + // switch to 16bpp + gp2x_video_changemode2(16); gp2x_video_RGB_setscaling(0, 320, 240); - gp2x_video_flip2(); + menu_flip(); } @@ -1858,20 +1975,20 @@ void menu_loop(void) static void draw_menu_tray(int menu_sel) { int tl_x = 70, tl_y = 90, y; - memset(gp2x_screen, 0xe0, 320*240); + memset(gp2x_screen, 0, 320*240*2); - gp2x_text_out8(tl_x, 20, "The unit is about to"); - gp2x_text_out8(tl_x, 30, "close the CD tray."); + text_out16(tl_x, 20, "The unit is about to"); + text_out16(tl_x, 30, "close the CD tray."); y = tl_y; - gp2x_text_out8(tl_x, y, "Load new CD image"); - gp2x_text_out8(tl_x, (y+=10), "Insert nothing"); + text_out16(tl_x, y, "Load new CD image"); + text_out16(tl_x, (y+=10), "Insert nothing"); // draw cursor - gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + text_out16(tl_x - 16, tl_y + menu_sel*10, ">"); // error - if (menuErrorMsg[0]) gp2x_text_out8(5, 226, menuErrorMsg); - gp2x_video_flip2(); + if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg); + menu_flip(); } diff --git a/platform/gp2x/menu.h b/platform/gp2x/menu.h index 8c9e29b..62ec051 100644 --- a/platform/gp2x/menu.h +++ b/platform/gp2x/menu.h @@ -1,13 +1,12 @@ -// (c) Copyright 2006 notaz, All rights reserved. +// (c) Copyright 2006,2007 notaz, All rights reserved. // Free for non-commercial use. // For commercial use, separate licencing terms must be obtained. extern char menuErrorMsg[40]; -void gp2x_text_out8 (int x, int y, const char *texto, ...); -void gp2x_text_out15 (int x, int y, const char *text); -void gp2x_text_out8_2(int x, int y, const char *texto, int color); +void menu_init(void); +void text_out16(int x, int y, const char *texto, ...); void menu_loop(void); int menu_loop_tray(void); void menu_romload_prepare(const char *rom_name); diff --git a/platform/gp2x/readpng.c b/platform/gp2x/readpng.c new file mode 100644 index 0000000..a890e4e --- /dev/null +++ b/platform/gp2x/readpng.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include "readpng.h" + +void readpng(void *dest, const char *fname, readpng_what what) +{ + FILE *fp; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_bytepp row_ptr = NULL; + + if (dest == NULL || fname == NULL) + { + return; + } + + fp = fopen(fname, "rb"); + if (fp == NULL) + { + printf(__FILE__ ": failed to open: %s\n", fname); + return; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + printf(__FILE__ ": png_create_read_struct() failed\n"); + fclose(fp); + return; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + printf(__FILE__ ": png_create_info_struct() failed\n"); + goto done; + } + + // Start reading + png_init_io(png_ptr, fp); + png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_PACKING, NULL); + row_ptr = png_get_rows(png_ptr, info_ptr); + if (row_ptr == NULL) + { + printf(__FILE__ ": png_get_rows() failed\n"); + goto done; + } + + // printf("%s: %ix%i @ %ibpp\n", fname, (int)info_ptr->width, (int)info_ptr->height, info_ptr->pixel_depth); + + switch (what) + { + case READPNG_BG: + { + int height, width, h; + unsigned short *dst = dest; + if (info_ptr->pixel_depth != 24) + { + printf(__FILE__ ": bg image uses %ibpp, needed 24bpp\n", info_ptr->pixel_depth); + break; + } + height = info_ptr->height; + if (height > 240) height = 240; + width = info_ptr->width; + if (width > 320) width = 320; + + for (h = 0; h < height; h++) + { + unsigned char *src = row_ptr[h]; + int len = width; + while (len--) + { + *dst++ = ((src[0]&0xf8)<<8) | ((src[1]&0xf8)<<3) | (src[2] >> 3); + src += 3; + } + dst += 320 - width; + } + break; + } + + case READPNG_FONT: + { + int x, y, x1, y1; + unsigned char *dst = dest; + if (info_ptr->width != 128 || info_ptr->height != 160) + { + printf(__FILE__ ": unexpected font image size %ix%i, needed 128x160\n", + (int)info_ptr->width, (int)info_ptr->height); + break; + } + if (info_ptr->pixel_depth != 8) + { + printf(__FILE__ ": font image uses %ibpp, needed 8bpp\n", info_ptr->pixel_depth); + break; + } + for (y = 0; y < 16; y++) + { + for (x = 0; x < 16; x++) + { + for (y1 = 0; y1 < 10; y1++) + { + unsigned char *src = row_ptr[y*10 + y1] + x*8; + for (x1 = 8/2; x1 > 0; x1--, src+=2) + *dst++ = ((src[0]^0xff) & 0xf0) | ((src[1]^0xff) >> 4); + } + } + } + break; + } + + case READPNG_SELECTOR: + { + int x1, y1; + unsigned char *dst = dest; + if (info_ptr->width != 8 || info_ptr->height != 10) + { + printf(__FILE__ ": unexpected selector image size %ix%i, needed 8x10\n", + (int)info_ptr->width, (int)info_ptr->height); + break; + } + if (info_ptr->pixel_depth != 8) + { + printf(__FILE__ ": selector image uses %ibpp, needed 8bpp\n", info_ptr->pixel_depth); + break; + } + for (y1 = 0; y1 < 10; y1++) + { + unsigned char *src = row_ptr[y1]; + for (x1 = 8/2; x1 > 0; x1--, src+=2) + *dst++ = ((src[0]^0xff) & 0xf0) | ((src[1]^0xff) >> 4); + } + break; + } + } + + +done: + png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : NULL, (png_infopp)NULL); + fclose(fp); +} + + diff --git a/platform/gp2x/readpng.h b/platform/gp2x/readpng.h new file mode 100644 index 0000000..542a774 --- /dev/null +++ b/platform/gp2x/readpng.h @@ -0,0 +1,10 @@ +typedef enum +{ + READPNG_BG = 1, + READPNG_FONT, + READPNG_SELECTOR +} +readpng_what; + +void readpng(void *dest, const char *fname, readpng_what what); + diff --git a/platform/linux/Makefile b/platform/linux/Makefile index 12b8860..5c85c5c 100644 --- a/platform/linux/Makefile +++ b/platform/linux/Makefile @@ -24,7 +24,8 @@ COPT += `pkg-config --cflags gthread-2.0` LDFLAGS += `pkg-config --libs gthread-2.0` # frontend -OBJS += ../gp2x/main.o ../gp2x/menu.o ../gp2x/fonts.o ../gp2x/emu.o ../gp2x/usbjoy.o blit.o gp2x.o 940ctl_ym2612.o +OBJS += ../gp2x/main.o ../gp2x/menu.o ../gp2x/fonts.o ../gp2x/emu.o ../gp2x/usbjoy.o blit.o \ + gp2x.o 940ctl_ym2612.o ../gp2x/readpng.o # Pico OBJS += ../../Pico/Area.o ../../Pico/Cart.o ../../Pico/Utils.o ../../Pico/Memory.o ../../Pico/Misc.o \ ../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o \ @@ -66,7 +67,7 @@ tidy: PicoDrive : $(OBJS) ../gp2x/helix/helix_mp3_x86.a @echo $@ - @$(GCC) $(COPT) $^ $(LDFLAGS) -lm -Wl,-Map=PicoDrive.map -o $@ + @$(GCC) $(COPT) $^ $(LDFLAGS) -lm -lpng -Wl,-Map=PicoDrive.map -o $@ ../../cpu/mz80/mz80.o : ../../cpu/mz80/mz80.asm diff --git a/platform/linux/gp2x.c b/platform/linux/gp2x.c index 199d69e..0a1c73b 100644 --- a/platform/linux/gp2x.c +++ b/platform/linux/gp2x.c @@ -257,13 +257,18 @@ void gp2x_video_setpalette(int *pal, int len) memcpy(current_pal, pal, len*4); } +void gp2x_video_flush_cache(void) +{ +} + void gp2x_video_RGB_setscaling(int v_offs, int W, int H) { } void gp2x_memcpy_buffers(int buffers, void *data, int offset, int len) { - memcpy((char *)gp2x_screen + offset, data, len); + if ((char *)gp2x_screen + offset != data) + memcpy((char *)gp2x_screen + offset, data, len); } void gp2x_memcpy_all_buffers(void *data, int offset, int len) @@ -279,7 +284,7 @@ void gp2x_memset_all_buffers(int offset, int byte, int len) void gp2x_pd_clone_buffer2(void) { - memset(gp2x_screen, 0, 320*240); + memset(gp2x_screen, 0, 320*240*2); } /* sound */ diff --git a/platform/readme.txt b/platform/readme.txt index 9a73187..4f4df8a 100644 --- a/platform/readme.txt +++ b/platform/readme.txt @@ -243,6 +243,8 @@ Changelog + Config profiles added (press left/right when saving config). * Changed key configuration behaviour to the one from gpfce (should be more intuitive). + + Added some skinning capabilities to the menu system with default skin by + ketch. Delete skin directory if you want old behaviour. 1.32 + Added some new scaling options. diff --git a/skin/background.png b/skin/background.png new file mode 100644 index 0000000000000000000000000000000000000000..7a1f5d313423ad13bce569e7fb77aa8527338411 GIT binary patch literal 44615 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�-fI7 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 XqVG$syRXKc00000NkvXXu0mjfg_gw( literal 0 HcmV?d00001 diff --git a/skin/selector.png b/skin/selector.png new file mode 100644 index 0000000000000000000000000000000000000000..5062cc23b08bd35ebbe7c5dca0c10ae479103c56 GIT binary patch literal 261 zcmeAS@N?(olHy`uVBq!ia0vp^96-zkA{f@Jafks@+9j?LCC){ui6xo&c?uz!xv31^ zB??By3PI@w3PuJ7<_d-;Rt6SUMrI1y20;F2g|&S^ZR{nUzOL-oImEd+jkE=q{{RYc z7I;J!Gca%qfiUBxyLEqnf-LEdzK#qG8~eHcB(ehev7RoDAr-fh{~RxP%DLW-pZWCr zo!jgFC7ivOXlD2K_3`iDpYNF=YFG3B+uQT$vD5tH>;L@w{QSMX+Eab|`oF)G{8J%^uZPiw_?7(a*>