1: /*
2: * Guitar-ZyX(tm)::MasterControlProgram - portable guitar F/X controller
3: * Copyright (C) 2009 Douglas McClendon
4: *
5: * This program is free software: you can redistribute it and/or modify
6: * it under the terms of the GNU General Public License as published by
7: * the Free Software Foundation, version 3 of the License.
8: *
9: * This program is distributed in the hope that it will be useful,
10: * but WITHOUT ANY WARRANTY; without even the implied warranty of
11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: * GNU General Public License for more details.
13: *
14: * You should have received a copy of the GNU General Public License
15: * along with this program. If not, see <http://www.gnu.org/licenses/>.
16: */
17: /*
18: #############################################################################
19: #############################################################################
20: ##
21: ## gzmcpc::mode__main_menu: the main menu(/command-tree) system
22: ##
23: #############################################################################
24: ##
25: ## Copyright 2008-2009 Douglas McClendon <dmc AT filteredperception DOT org>
26: ##
27: #############################################################################
28: #############################################################################
29: #
30: */
31:
32:
33:
34:
35: #include <nds.h>
36:
37:
38: #include <nds/bios.h>
39:
40: #include <stdio.h>
41:
42: #include <string.h>
43:
44: #include <dirent.h>
45:
46:
47: #include "cloader/arm9_loader.h"
48:
49: #include "dmc.h"
50:
51: #include "debug.h"
52:
53: #include "graphics.h"
54:
55: #include "input.h"
56:
57: #include "main.h"
58:
59: #include "mcp.h"
60:
61: #include "modes.h"
62:
63: #include "mode__intro__main.h"
64:
65: #include "mode__get_update.h"
66:
67: #include "mode__main_menu.h"
68:
69: #include "network.h"
70:
71: #include "sound.h"
72:
73:
74: #include "sounds.h"
75:
76: #include "sounds_bin.h"
77:
78: #include "resources/bitmaps/guitar-zyx.splash.main.h"
79:
80: #include "resources/bitmaps/dlava.h"
81:
82: #include "resources/bitmaps/mcpfont.h"
83:
84:
85:
86:
87:
88: gzmcp_menu_node *mm_list = NULL;
89: gzmcp_menu_node *mm_hist = NULL;
90:
91:
92:
93: ConsoleFont mcpfont;
94:
95: char pwd[FS_PATH_MAXLEN + 1];
96: char t_scratch_path[FS_PATH_MAXLEN + 1];
97:
98: int selection_index = 0;
99: int num_selections = 0;
100:
101: int hilite_offset = 0;
102:
103: int mm_doublemode = 0;
104: int mm_dm_height_lines = 0;
105: int mm_dm_scroll_pad = 0;
106: int mm_dm_repeat_rate = 0;
107: int mm_dm_width = 0;
108: int mm_dm_xscl = 0;
109: int mm_dm_yscl = 0;
110:
111:
112: int mm_text_offset_target = 0;
113: int mm_text_offset_actual = 0;
114: int mm_hilite_offset_relative = 0;
115:
116: u16 mm_txt_fontcolor = MM_TXT_DEFAULTCOLOR;
117: u16 mm_hilitecolor = MM_HILITE_DEFAULTCOLOR;
118:
119: int mm_txt_firstfade_done;
120: int mm_txt_render_hilite;
121:
122:
123:
124:
125:
126: void mode__main_menu___init(void) {
127:
128:
129: // initialize fading system
130: mcp_fade_init();
131:
132: // initialize main screen
133: videoSetMode(MODE_5_2D);
134:
135: // map main screen background fourth (128k) region to vram bank A
136: vramSetBankA(VRAM_A_MAIN_BG_0x06060000);
137:
138: // set the secondary/sub screen for text and a background
139: videoSetModeSub(MODE_5_2D);
140:
141: // map sub screen background (only? 1/4?) to vram bank C
142: vramSetBankC(VRAM_C_SUB_BG);
143:
144: // unfaded
145: mcp_set_blend(MCP_MAIN_SCREEN,
146: MCP_MAX_BLEND_LEVEL);
147: mcp_set_blend(MCP_SUB_SCREEN,
148: MCP_MAX_BLEND_LEVEL);
149:
150: // fade the mainscreen background to/from black, layer 3
151: REG_BLDCNT = BLEND_FADE_BLACK | BLEND_SRC_BG3;
152: // fade the lava background to/from black, layer 2
153: REG_BLDCNT_SUB = BLEND_FADE_BLACK | BLEND_SRC_BG2;
154:
155: // visible fonts
156: BG_PALETTE[255] = RGB15(0, 0, 0);
157: BG_PALETTE_SUB[255] = RGB15(0, 0, 0);
158:
159: // init subscreen layer/background 3
160: // the mapbase offset of 24 here means 24*16k which means utilizing
161: // the 4th of the possible main background memory regions that vram
162: // bank A can be mapped to. I.e. above we mapped to the 4th. Had
163: // we mapped to the 1st, we would have used offset 0.
164: // note: vram bank A is 128k, i.e. 8 * 16k.
165: // note: *16k is because of bitmap type, else would be *2k
166: // bg3 = bgInit(3, BgType_Bmp16, BgSize_B16_256x256, 24, 0);
167: bg3 = mcp_bg_init(MCP_MAIN_SCREEN,
168: 3,
169: MCP_BG_HIDE,
170: BgType_Bmp16,
171: BgSize_B16_256x256,
172: 24,
173: 0);
174: // its initial priority, lowest (to emphasize lack of other enabled layers)
175: // priorities 0..3, 0 highest priority
176: bgSetPriority(bg3, 3);
177:
178: // load main splash screen into screen/background memory (bgs3)
179: // note: if I wanted to do this quickly/perfectly/doublebufferred somehow
180: // I'm not sure if I'd need to tweak the bgInitSub offset or pointer
181: // here or some such. As it is, the fade to black ensures no tear
182: // as this new image gets loaded into display memory.
183: decompress(guitar_zyx_splash_mainBitmap,
184: (u16*)bgGetGfxPtr(bg3),
185: LZ77Vram);
186:
187: bgShow(bg3);
188:
189: // note: using offset=4, because 4 will be 64k offset, where 31 above is 62k
190: // (thus above using only 2k? seems plausible with tiles for console text chars
191: // bgs2 = bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 4, 0);
192: bgs2 = mcp_bg_init(MCP_SUB_SCREEN,
193: 2,
194: MCP_BG_HIDE,
195: BgType_Bmp8,
196: BgSize_B8_256x256,
197: 4,
198: 0);
199: // its initial priority, lowest (to emphasize lack of other enabled layers)
200: // priorities 0..3, 0 highest priority
201: bgSetPriority(bgs2, 2);
202:
203: // as per libnds doc on dma
204: DC_FlushRange(MM_BG_BMP, 256*256);
205: dmaCopy(MM_BG_BMP, bgGetGfxPtr(bgs2), 256*256);
206: DC_FlushRange(MM_BG_PAL, 256*2);
207: dmaCopy(MM_BG_PAL, BG_PALETTE_SUB, 256*2);
208:
209: bgShow(bgs2);
210:
211: // edunote: consoleInit/mcp_console_init overwrite completely
212: // the PrintConsole arg
213:
214: // was 31, 0 for normal non 8bpp font,
215: // 20, 0 for 8bpp font
216: // ,1 is to to avoid corruption
217:
218: // note: no need to load graphics
219: mcp_console_init(&bottom_screen,
220: MCP_SUB_SCREEN,
221: 3,
222: 1,
223: 1,
224: BgType_ExRotation,
225: BgSize_ER_256x256,
226: 31,
227: 1);
228:
229: // set printf sink
230: consoleSelect(&bottom_screen);
231:
232: // custom 8bpp font
233: mcpfont.asciiOffset = 32;
234: mcpfont.bpp = 8;
235: mcpfont.convertSingleColor = false;
236: mcpfont.gfx = (u16*)mcpfontTiles;
237: mcpfont.numChars = 95;
238: mcpfont.numColors = mcpfontPalLen / 2;
239: mcpfont.pal = (u16*)mcpfontPal;
240: consoleSetFont(&bottom_screen, &mcpfont);
241:
242: // set console background layer to top priority
243: bgSetPriority(bottom_screen.bgId, 0);
244:
245: // this triggers the initial mode fade in
246: mm_txt_firstfade_done = 0;
247: mm_txt_render_hilite = 0;
248:
249: // initialize to black text
250: BG_PALETTE_SUB[MM_FONT_COLOR_INDEX] = RGB15(0, 0, 0);
251:
252: // initialize pwd
253: // just using sn out of habit
254: snprintf(pwd, FS_PATH_MAXLEN, "/");
255:
256: //
257: // initialize the menu
258: //
259: // mm_hist_push("WhammyPad!");
260: // mm_create_root();
261: mm_create_last();
262:
263: }
264:
265:
266: void mode__main_menu___top_renderer(void) {
267:
268: int t_blend;
269:
270: //
271: // initialize to unfaded values
272: //
273: t_blend = 0;
274:
275:
276: //
277: // do top background fade-in
278: //
279: if (mode_ms < MAIN_MENU__TOP_BG_FADE_IN_START_MS) {
280: t_blend = 31;
281: } else if (mode_ms < (MAIN_MENU__TOP_BG_FADE_IN_START_MS +
282: MAIN_MENU__TOP_BG_FADE_IN_DURATION_MS)) {
283: // main screen bg fade
284: t_blend = 31 - ((mode_ms - MAIN_MENU__TOP_BG_FADE_IN_START_MS) * 31 /
285: MAIN_MENU__TOP_BG_FADE_IN_DURATION_MS);
286:
287: }
288:
289: //
290: // handle fadeout
291: //
292: if (mode != next_mode) {
293: if ((mode_ms - exit_mode_ms) < MAIN_MENU__TOP_BG_FADE_OUT_DURATION_MS) {
294: t_blend = ((mode_ms - exit_mode_ms) *
295: 31 / MAIN_MENU__TOP_BG_FADE_OUT_DURATION_MS);
296: } else {
297: t_blend = 31;
298: }
299: // fadeouts should only be fading out
300: // i.e. this code may be executing right after an aborted fadein
301: // t_blend = MAX(REG_BLDY, t_blend);
302: t_blend = MAX(t_blend, mcp_get_blend(MCP_MAIN_SCREEN));
303: } // end handle fadeout
304:
305: // actually set the blend register value
306: mcp_set_blend(MCP_MAIN_SCREEN,
307: t_blend);
308:
309: }
310:
311:
312: void mode__main_menu___bot_renderer(void) {
313:
314: int t_blend;
315:
316: if (mode_ms < MAIN_MENU__BOT_TXT_FADE_IN_START_MS) {
317: } else {
318: if (!mm_txt_firstfade_done) {
319: mm_txt_render_hilite = 1;
320: BG_PALETTE_SUB[MM_FONT_COLOR_INDEX] = RGB15(0, 0, 0);
321: mcp_fade_set_level(MCP_SUB_SCREEN, 3, 0);
322: mcp_bg_show(MCP_SUB_SCREEN, 3);
323: mcp_fade_start(MCP_SUB_SCREEN,
324: 3,
325: 1,
326: MAIN_MENU__BOT_TXT_FADE_IN_DURATION_MS,
327: idle_rate,
328: NULL);
329: mm_txt_firstfade_done = 1;
330: }
331: }
332:
333: // clear the console text
334: consoleClear();
335: // and hilite
336: render_hilite(bgs2, 0, 0, 0, 0, 0, 1);
337:
338: // set console variable parameters
339: // note: -128, -128 is doubling
340: bgSetRotateScale(bottom_screen.bgId,
341: 0,
342: intToFixed(1,8) + mm_dm_xscl,
343: intToFixed(1,8) + mm_dm_yscl);
344:
345: // TODO: verbosely document this ugly crap
346: // the -4 is down half an 8 pixel char (that has scaled to 16pix)
347: // the other term is another optional same amount
348: bgScroll(bottom_screen.bgId,
349: 0,
350: -4 - (mm_text_offset_target & 15));
351: bgUpdate();
352:
353:
354: //
355: // initialize to unfaded values
356: //
357: t_blend = 0;
358:
359: //
360: // do bg fade-in
361: //
362: if (mode_ms < MAIN_MENU__BOT_BG_FADE_IN_START_MS) {
363: // pre fade-in
364: t_blend = 31;
365: } else if (mode_ms < (MAIN_MENU__BOT_BG_FADE_IN_START_MS +
366: MAIN_MENU__BOT_BG_FADE_IN_DURATION_MS)) {
367: // main screen bg fade-in
368: t_blend = 31 - ((mode_ms - MAIN_MENU__BOT_BG_FADE_IN_START_MS) * 31 /
369: MAIN_MENU__BOT_BG_FADE_IN_DURATION_MS);
370:
371: }
372:
373:
374: //
375: // do fadeout, possibly overriding above
376: //
377: if (mode != next_mode) {
378: // start a fade-out if a fade isn't in progress, or is fading in,
379: if (!mcp_fade_get_enabled(MCP_SUB_SCREEN, 3) ||
380: mcp_fade_get_fwd(MCP_SUB_SCREEN, 3)) {
381: mcp_fade_start(MCP_SUB_SCREEN,
382: 3,
383: 0,
384: MAIN_MENU__BOT_TXT_FADE_OUT_DURATION_MS,
385: idle_rate,
386: NULL);
387: }
388:
389: // fade-out
390: if ((mode_ms - exit_mode_ms) < MAIN_MENU__BOT_BG_FADE_OUT_DURATION_MS) {
391: t_blend = ((mode_ms - exit_mode_ms) *
392: 31 / MAIN_MENU__BOT_BG_FADE_OUT_DURATION_MS);
393: } else {
394: t_blend = 31;
395: }
396: // only fadeout
397: t_blend = MAX(t_blend, mcp_get_blend(MCP_SUB_SCREEN));
398:
399: }
400:
401: mcp_set_blend(MCP_SUB_SCREEN,
402: t_blend);
403:
404: /*
405: // clear the console text
406: // consoleClear();
407: // show the entire font
408: int i, x, y;
409: // 32 + 95, now 60 + 9
410: for (i = 32; i < (32 + 95); i++) {
411: y = (i - 32) / 12;
412: x = (i - 32) % 12;
413: printf("\x1b[%02d;%02dH%c",
414: y, x, i);
415: }
416: */
417:
418: // print/render the main menu labels
419: mm_print();
420:
421: if (mm_txt_render_hilite) {
422: render_hilite(bgs2,
423: RGB15(RGB15_TO_R5(mm_hilitecolor) * mcp_fade_get_level(MCP_SUB_SCREEN, 3) / MCP_FADE_MAXLEVEL,
424: RGB15_TO_G5(mm_hilitecolor) * mcp_fade_get_level(MCP_SUB_SCREEN, 3) / MCP_FADE_MAXLEVEL,
425: RGB15_TO_B5(mm_hilitecolor) * mcp_fade_get_level(MCP_SUB_SCREEN, 3) / MCP_FADE_MAXLEVEL),
426: hilite_offset,
427: SCREEN_WIDTH * mm_dm_width / 32,
428: SCREEN_HEIGHT * 3 / 2 / mm_dm_height_lines,
429: SCREEN_HEIGHT / mm_dm_height_lines / 2,
430: 0);
431: }
432:
433: // set the fade by changing the font's palette entry
434: BG_PALETTE_SUB[MM_FONT_COLOR_INDEX] =
435: RGB15(RGB15_TO_R5(mm_txt_fontcolor) * mcp_fade_get_level(MCP_SUB_SCREEN, 3) / MCP_FADE_MAXLEVEL,
436: RGB15_TO_G5(mm_txt_fontcolor) * mcp_fade_get_level(MCP_SUB_SCREEN, 3) / MCP_FADE_MAXLEVEL,
437: RGB15_TO_B5(mm_txt_fontcolor) * mcp_fade_get_level(MCP_SUB_SCREEN, 3) / MCP_FADE_MAXLEVEL);
438:
439: }
440:
441:
442: void mode__main_menu___input_handler(void) {
443:
444: //
445: // prepare input, i.e. transmogrify special held situations
446: // into downkeys
447: //
448:
449: // only bother if sunset has expired
450: if (time_val_compare(num_ticks, heldover_sunset) < 0) {
451:
452: if ((heldkeys & KEY_L) && (heldkeys & KEY_R)) {
453: //
454: // held(L)+held(R)+something
455: //
456:
457: if (heldkeys & KEY_LEFT) {
458: downkeys |= KEY_LEFT;
459: }
460:
461: if (heldkeys & KEY_RIGHT) {
462: downkeys |= KEY_RIGHT;
463: }
464:
465: if (heldkeys & KEY_UP) {
466: downkeys |= KEY_UP;
467: }
468:
469: if (heldkeys & KEY_DOWN) {
470: downkeys |= KEY_DOWN;
471: }
472:
473: } else if (heldkeys & KEY_L) {
474:
475: if (heldkeys & KEY_A) {
476: downkeys |= KEY_A;
477: }
478:
479: if (heldkeys & KEY_B) {
480: downkeys |= KEY_B;
481: }
482:
483: if (heldkeys & KEY_X) {
484: downkeys |= KEY_X;
485: }
486:
487: if (heldkeys & KEY_Y) {
488: downkeys |= KEY_Y;
489: }
490:
491: if (heldkeys & KEY_LEFT) {
492: downkeys |= KEY_LEFT;
493: }
494:
495: if (heldkeys & KEY_RIGHT) {
496: downkeys |= KEY_RIGHT;
497: }
498:
499: if (heldkeys & KEY_UP) {
500: downkeys |= KEY_UP;
501: }
502:
503: if (heldkeys & KEY_DOWN) {
504: downkeys |= KEY_DOWN;
505: }
506:
507: } else if (heldkeys & KEY_R) {
508:
509: if (heldkeys & KEY_A) {
510: downkeys |= KEY_A;
511: }
512:
513: if (heldkeys & KEY_B) {
514: downkeys |= KEY_B;
515: }
516:
517: if (heldkeys & KEY_X) {
518: downkeys |= KEY_X;
519: }
520:
521: if (heldkeys & KEY_Y) {
522: downkeys |= KEY_Y;
523: }
524:
525: if (heldkeys & KEY_LEFT) {
526: downkeys |= KEY_LEFT;
527: }
528:
529: if (heldkeys & KEY_RIGHT) {
530: downkeys |= KEY_RIGHT;
531: }
532:
533: if (heldkeys & KEY_UP) {
534: downkeys |= KEY_UP;
535: }
536:
537: if (heldkeys & KEY_DOWN) {
538: downkeys |= KEY_DOWN;
539: }
540:
541: } else {
542:
543: if (heldkeys & KEY_LEFT) {
544: downkeys |= KEY_LEFT;
545: }
546:
547: if (heldkeys & KEY_RIGHT) {
548: downkeys |= KEY_RIGHT;
549: }
550:
551: if (heldkeys & KEY_UP) {
552: downkeys |= KEY_UP;
553: }
554:
555: if (heldkeys & KEY_DOWN) {
556: downkeys |= KEY_DOWN;
557: }
558:
559: }
560:
561: } // end if heldover_sunset expired
562:
563: //
564: // handle input
565: //
566: if ((heldkeys & KEY_L) && (heldkeys & KEY_R)) {
567: //
568: // held(L)+held(R)+something
569: //
570:
571: } else if (heldkeys & KEY_L) {
572: //
573: // held(L)+something
574: //
575:
576: if (downkeys & KEY_X) {
577: }
578:
579: if (downkeys & KEY_Y) {
580: }
581:
582: if (downkeys & KEY_A) {
583: }
584:
585: if (downkeys & KEY_B) {
586: }
587:
588: if (downkeys & KEY_UP) {
589: }
590:
591: if (downkeys & KEY_DOWN) {
592: }
593:
594: if (downkeys & KEY_START) {
595: }
596:
597: } else if (heldkeys & KEY_R) {
598: //
599: // held(R)+something
600: //
601:
602: if (downkeys & KEY_UP) {
603: }
604:
605: if (downkeys & KEY_DOWN) {
606: }
607:
608: if (downkeys & KEY_LEFT) {
609: }
610:
611: if (downkeys & KEY_RIGHT) {
612: }
613:
614: } else {
615: //
616: // no interesting modifer keys held
617: //
618:
619: if (downkeys & KEY_START) {
620: if (last_mode == MODE_NUM_MODES) {
621: system_xmode_new(MODE_MAIN_MENU);
622: } else {
623: system_xmode_new(last_mode);
624: }
625: }
626:
627: if (downkeys & KEY_SELECT) {
628: }
629:
630: if (downkeys & KEY_UP) {
631: mm_set_selection_by_index(selection_index - 1);
632: heldover_sunset = time_val_add_ms(num_ticks, mm_dm_repeat_rate);
633: }
634:
635: if (downkeys & KEY_DOWN) {
636: mm_set_selection_by_index(selection_index + 1);
637: heldover_sunset = time_val_add_ms(num_ticks, mm_dm_repeat_rate);
638: }
639:
640: if (downkeys & KEY_LEFT) {
641: }
642:
643: if (downkeys & KEY_RIGHT) {
644: }
645:
646: if (downkeys & KEY_X) {
647: }
648:
649: if (downkeys & KEY_Y) {
650: }
651:
652: if (downkeys & KEY_A) {
653: // run selected select_cb
654: if (mm_get_node_by_index(selection_index) != NULL) {
655: // wow this is pretty gross
656: // don't add *(go back)* to the stack, as our selection of it
657: // always means we'd prefer whatever is already on the
658: // top of the stack.
659: if (strstr(mm_get_node_by_index(selection_index)->label,
660: "(go back)") == NULL) {
661: mm_hist_push(mm_get_node_by_index(selection_index)->label);
662: }
663: if (mm_get_node_by_index(selection_index)->select_cb != NULL) {
664: mcp_snd_click();
665: (*((mm_get_node_by_index(selection_index))->select_cb))();
666: }
667: }
668: }
669:
670: if (downkeys & KEY_B) {
671: }
672:
673: }
674:
675:
676: // touchpad handling is independent of modifiers (at the moment)
677: if ((downkeys & KEY_TOUCH) || (heldkeys & KEY_TOUCH)) {
678:
679: }
680:
681: }
682:
683: void mode__main_menu___idle(void) {
684: //
685: // handle fadeout
686: //
687: if (mode != next_mode) {
688: if (((mode_ms - exit_mode_ms) > MAIN_MENU__TOP_BG_FADE_OUT_DURATION_MS) &&
689: ((mode_ms - exit_mode_ms) > MAIN_MENU__BOT_BG_FADE_OUT_DURATION_MS) &&
690: ((mode_ms - exit_mode_ms) > MAIN_MENU__BOT_TXT_FADE_OUT_DURATION_MS)) {
691: system_xmode_real();
692: }
693: }
694: }
695:
696:
697: void mode__main_menu___exit(void) {
698:
699: mm_free();
700:
701: }
702:
703:
704: void mm_set_doublemode(int mode) {
705:
706: mm_doublemode = mode;
707: if (mode) {
708: mm_dm_width = 24;
709: mm_dm_height_lines = 12;
710: mm_dm_scroll_pad = 4;
711: mm_dm_repeat_rate = 200;
712: mm_dm_xscl = -128;
713: mm_dm_yscl = -128;
714: } else {
715: mm_dm_width = 30;
716: mm_dm_height_lines = 24;
717: mm_dm_scroll_pad = 8;
718: mm_dm_repeat_rate = 125;
719: mm_dm_xscl = 0;
720: mm_dm_yscl = 0;
721: }
722:
723: // XXX if this works, make this a function shared with other copy
724: // clear the console text
725: consoleClear();
726: // and the hilite
727: render_hilite(bgs2, 0, 0, 0, 0, 0, 1);
728:
729: // set console variable parameters
730: // note: -128, -128 is doubling
731: bgSetRotateScale(bottom_screen.bgId,
732: 0,
733: intToFixed(1,8) + mm_dm_xscl,
734: intToFixed(1,8) + mm_dm_yscl);
735:
736: // TODO: verbosely document this ugly crap
737: // the -4 is down half an 8 pixel char (that has scaled to 16pix)
738: // the other term is another optional same amount
739: bgScroll(bottom_screen.bgId,
740: 0,
741: -4 - (mm_text_offset_target & 15));
742: bgUpdate();
743:
744:
745: }
746:
747:
748: void render_hilite(int bgid,
749: int color,
750: int y_offset,
751: int width,
752: int height,
753: int roundness,
754: int unrender) {
755:
756: // ooh, ahh, static local variables put to good use
757: // edunote: in case I forget, static local variables in functions, which
758: // clearly I rarely use, are roughly equivalent to private globals,
759: // i.e. initialized once, and retaining value across multiple
760: // invocations of the function
761: static int y_offset_prev = 0;
762: // note: the dmaCopy doesn't behave as I'd expect with a length of 0
763: static int height_prev = 1;
764: static int dirty = 0;
765:
766: int i;
767: int rounded_width;
768:
769:
770: // note: bgGetGfxPtr returns u16*, but this is 8bpp data
771:
772: // edunote: brief net search suggests there is no advantage to using
773: // bitshifts instead of *2^n, as compiler will optimize while
774: // the code will remain more readable.
775:
776: if (unrender || dirty) {
777: // first, restore possible background damage from previous invocation
778: DC_FlushRange(((const void*)MM_BG_BMP) + (y_offset_prev * SCREEN_WIDTH),
779: height_prev * SCREEN_WIDTH);
780: dmaCopy(((const void*)MM_BG_BMP) + (y_offset_prev * SCREEN_WIDTH),
781: (void *)(bgGetGfxPtr(bgid)) + (y_offset_prev * SCREEN_WIDTH),
782: height_prev * SCREEN_WIDTH);
783: dirty = 0;
784: // unrender means that is all that should be done
785: if (unrender) return;
786: }
787:
788: // set the hilite color
789: BG_PALETTE_SUB[MM_HILITE_COLOR_INDEX] = color;
790:
791: // render/copy the top rounded portion of the highlite
792: for (i = y_offset ; i < (y_offset + roundness) ; i++) {
793: rounded_width = width - ((roundness - (i - y_offset)) * 2);
794: dmaFillHalfWords((MM_HILITE_COLOR_INDEX * SCREEN_WIDTH) + MM_HILITE_COLOR_INDEX,
795: ((void *)bgGetGfxPtr(bgid)) +
796: (i * SCREEN_WIDTH) + ((SCREEN_WIDTH - rounded_width) / 2),
797: rounded_width);
798: }
799:
800: // render/copy the middle unrounded portion of the highlite
801: for (i = (y_offset + roundness) ;
802: i < (y_offset + height - roundness) ;
803: i++) {
804: dmaFillHalfWords((MM_HILITE_COLOR_INDEX * SCREEN_WIDTH) + MM_HILITE_COLOR_INDEX,
805: ((void *)bgGetGfxPtr(bgid)) +
806: (i * SCREEN_WIDTH) + ((SCREEN_WIDTH - width) / 2),
807: width);
808: }
809:
810: // render/copy the bottom rounded portion of the highlite
811: for (i = (y_offset + height - roundness) ;
812: i < (y_offset + height) ;
813: i++) {
814: rounded_width = width - ((roundness - ((y_offset + height) - i)) * 2);
815: dmaFillHalfWords((MM_HILITE_COLOR_INDEX * SCREEN_WIDTH) + MM_HILITE_COLOR_INDEX,
816: ((void *)bgGetGfxPtr(bgid)) +
817: (i * SCREEN_WIDTH) + ((SCREEN_WIDTH - rounded_width) / 2),
818: rounded_width);
819: }
820:
821: // save the information about the part of the screen we just hilited,
822: // so that it can be restored next time.
823: y_offset_prev = y_offset;
824: height_prev = height;
825:
826: }
827:
828: void mm_add_node(const char *label,
829: int selectable,
830: void(*select_cb)(void)) {
831:
832: gzmcp_menu_node **p = &mm_list;
833:
834:
835: // get a pointer to final NULL pointer in the llist
836: while (*p != NULL) p = &((*p)->next);
837:
838: // allocate memory for a new node
839: *p = malloc(sizeof(gzmcp_menu_node));
840: if (*p == NULL) {
841: die();
842: }
843:
844: strncpy((*p)->label, label, MENU_LABEL_MAXLEN);
845: (*p)->select_cb = select_cb;
846: (*p)->selectable = selectable;
847: (*p)->next = NULL;
848:
849: num_selections++;
850: }
851:
852:
853: gzmcp_menu_node *mm_get_node_by_index(int index) {
854:
855: int i;
856: gzmcp_menu_node *result;
857:
858: result = mm_list;
859:
860: for (i = 1; i < index; i++) {
861: if (result->next == NULL) {
862: return NULL;
863: } else {
864: result = result->next;
865: }
866: }
867:
868: return result;
869:
870: }
871:
872:
873: int mm_get_index_by_label(const char *label) {
874:
875: int i;
876: int done = 0;
877: gzmcp_menu_node *node;
878:
879: node = mm_list;
880: i=1;
881:
882: while (!done) {
883: if (strncmp(node->label, label, MENU_LABEL_MAXLEN) == 0) return i;
884: i++;
885: node = node->next;
886: if (node == NULL) done = 1;
887: }
888:
889: return -1;
890: }
891:
892:
893: void mm_alphasort(gzmcp_menu_node *head,
894: int from_index,
895: int to_index) {
896:
897: gzmcp_menu_node *t_mm_node_lower_parent;
898: gzmcp_menu_node *t_mm_node_lower;
899: gzmcp_menu_node *t_mm_node_upper_parent;
900: gzmcp_menu_node *t_mm_node_upper;
901: gzmcp_menu_node *t_mm_node_ptr;
902: int t_cnt;
903: int i, j, k;
904:
905:
906: // current logic cannot change the first entry, which
907: // by convention is '(go back)' anyway
908: if (from_index <= 1) return;
909:
910: // note: for now, ignore selection, assuming that
911: // alphasort will be called after menu creation,
912: // but before selection initialization
913:
914: t_cnt = from_index;
915:
916: for (i = from_index ; i <= to_index ; i++) {
917:
918: for (j = from_index ; j < to_index ; j++) {
919:
920: t_mm_node_lower_parent = mm_get_node_by_index(j - 1);
921: t_mm_node_lower = mm_get_node_by_index(j);
922: // these two are mainly to silence - possible uninitialized use warnings
923: t_mm_node_upper_parent = mm_get_node_by_index(j);
924: t_mm_node_upper = mm_get_node_by_index(j + 1);
925:
926: if (!(t_mm_node_lower->selectable)) continue;
927: if (t_mm_node_lower->next == NULL) continue;
928:
929: for (k = (j + 1); k <= to_index ; k++) {
930: t_mm_node_upper = mm_get_node_by_index(k);
931: if (t_mm_node_upper->selectable) break;
932: }
933:
934: if (t_mm_node_upper->selectable) {
935: t_mm_node_upper_parent = mm_get_node_by_index(k - 1);
936: // compare the node's labels
937: if (strncmp(t_mm_node_lower->label,
938: t_mm_node_upper->label,
939: MENU_LABEL_MAXLEN) > 0) {
940: // swap
941: t_mm_node_ptr = t_mm_node_lower->next;
942: t_mm_node_lower->next = t_mm_node_upper->next;
943: t_mm_node_upper->next = t_mm_node_ptr;
944:
945: t_mm_node_ptr = t_mm_node_lower_parent->next;
946: t_mm_node_lower_parent->next = t_mm_node_upper_parent->next;
947: t_mm_node_upper_parent->next = t_mm_node_ptr;
948:
949: } // end if need to actually do a bubble swap
950:
951: } // end if we have a selectable node to possibly bubble swap
952:
953: } // end of a single pass of bubbling iteration
954:
955: } // end outtermost bubble iteration
956:
957: }
958:
959:
960: void mm_free(void) {
961:
962: gzmcp_menu_node *t_mm_node;
963:
964:
965: // free the list entry by entry
966: while (mm_list != NULL) {
967: t_mm_node = mm_list->next;
968: free(mm_list);
969: mm_list = t_mm_node;
970: num_selections--;
971: }
972:
973: // initialize offsets
974: mm_text_offset_target = (0 << 4) | 0;
975: mm_text_offset_actual = (0 << 4) | 0;
976: mm_hilite_offset_relative = (0 << 4) | 0;
977:
978: // and selection_index
979: selection_index = 0;
980:
981: }
982:
983:
984: void mm_set_selection_by_index(int index) {
985:
986: // for finding selectable index
987: int found = 0;
988: // for recording previous selected index
989: int prev_index;
990: // stack variables could be removed, but code would be less readable
991: int offset_lines_fraction;
992: int offset_lines;
993:
994:
995: // record prior selection for rendering offset calculation below
996: prev_index = selection_index;
997:
998: // do nothing if there is nothing to do
999: if (num_selections < 1) return;
1000:
1001: // can't go out of bounds
1002: // assert candidate
1003: if ((index < 1) || (index > num_selections)) return;
1004:
1005: #define PRV_OFFSET (mm_text_offset_target >> 4)
1006: #define VIS_ENTRIES_MAX (mm_dm_height_lines - 1)
1007: #define CUR_INNER_REGION_MAX PRV_OFFSET + VIS_ENTRIES_MAX - mm_dm_scroll_pad
1008: #define CUR_INNER_REGION_MIN PRV_OFFSET + mm_dm_scroll_pad + 1
1009:
1010: //
1011: // set new index (skipping unselectable entries)
1012: //
1013: selection_index = index;
1014: if (selection_index < prev_index) {
1015: while (!found) {
1016: if (mm_get_node_by_index(selection_index)->selectable) {
1017: found = 1;
1018: } else if (selection_index == 1) {
1019: // give up, no selectable entry found, use previous
1020: selection_index = prev_index;
1021: found = 1;
1022: } else {
1023: // try the next entry above
1024: selection_index--;
1025: }
1026: } // end while (!found)
1027: } else if (selection_index > prev_index) {
1028: while (!found) {
1029: if (mm_get_node_by_index(selection_index)->selectable) {
1030: found = 1;
1031: } else if (selection_index == num_selections) {
1032: // give up, no selectable entry found, use previous
1033: selection_index = prev_index;
1034: found = 1;
1035: } else {
1036: // try the next entry above
1037: selection_index++;
1038: }
1039: } // end while (!found)
1040: } else {
1041: // no change in selection
1042: return;
1043: }
1044:
1045: //
1046: // handle many possible scenarios, the union of which is all possibilities
1047: //
1048:
1049:
1050: if (num_selections < VIS_ENTRIES_MAX) {
1051: // if the number of selections need not be scrolled,
1052: // adjust so that the offset vertically centers the entries
1053: offset_lines_fraction = (num_selections % 2) * 8;
1054: // this logic needs to be rewritten and better understood
1055: offset_lines = ((VIS_ENTRIES_MAX - num_selections - 1) / -2);
1056: } else if ((selection_index <= CUR_INNER_REGION_MAX) &&
1057: (selection_index >= CUR_INNER_REGION_MIN)) {
1058: // if the new index is in the existing visible region,
1059: // no adjustment is necessary
1060: offset_lines_fraction = mm_text_offset_target & 15;
1061: offset_lines = mm_text_offset_target >> 4;
1062: } else if (selection_index <= mm_dm_scroll_pad) {
1063: // handle case where scroll should be at the top
1064: offset_lines_fraction = 0;
1065: offset_lines = 0;
1066: } else if (selection_index >= (num_selections - mm_dm_scroll_pad)) {
1067: // handle case where scroll should be at the bottom
1068: offset_lines_fraction = 0;
1069: offset_lines = num_selections - VIS_ENTRIES_MAX;
1070: } else if (selection_index > prev_index) {
1071: // scroll down
1072: offset_lines_fraction = 0;
1073: offset_lines = selection_index -VIS_ENTRIES_MAX + mm_dm_scroll_pad;
1074: } else if (selection_index < prev_index) {
1075: // scroll up
1076: offset_lines_fraction = 0;
1077: offset_lines = selection_index - mm_dm_scroll_pad - 1;
1078: } else {
1079: // should be an assertion
1080: offset_lines_fraction = 0;
1081: offset_lines = -42;
1082: }
1083:
1084: // set the new compact mm_text_offset_target from calculated values
1085: mm_text_offset_target = (offset_lines << 4) | offset_lines_fraction;
1086:
1087: // int mm_text_offset_actual = (0 << 4) | 0;
1088: // int mm_hilite_offset_relative = (0 << 4) | 0;
1089:
1090: hilite_offset = ((selection_index - offset_lines - 1) *
1091: ((SCREEN_HEIGHT) / mm_dm_height_lines)) + (SCREEN_HEIGHT / (mm_dm_height_lines * 4));
1092: hilite_offset += (offset_lines_fraction * ((SCREEN_HEIGHT / mm_dm_height_lines) / 8));
1093:
1094: // user audio feedback / click (quarter volume)
1095: if (prev_index && (selection_index != prev_index)) mcp_snd_click();
1096:
1097: }
1098:
1099:
1100: void mm_set_selection_by_label(const char *label) {
1101:
1102: // TODO: catch error/-1 from get_index_by_label
1103: if (mm_get_index_by_label(label) == -1) {
1104: mm_set_selection_by_index(1);
1105: } else {
1106: mm_set_selection_by_index(mm_get_index_by_label(label));
1107: }
1108:
1109: }
1110:
1111:
1112: void mm_set_selection_by_hist(void) {
1113:
1114: while (mm_get_index_by_label(mm_hist_peek()) == -1) {
1115: mm_hist_pop();
1116: if (mm_hist == NULL) break;
1117: }
1118:
1119: if (mm_hist == NULL) {
1120: mm_set_selection_by_index(1);
1121: } else {
1122: selection_index = 0;
1123: mm_set_selection_by_index(mm_get_index_by_label(mm_hist_peek()));
1124: mm_hist_pop();
1125: }
1126:
1127: }
1128:
1129:
1130: void mm_print(void) {
1131:
1132: gzmcp_menu_node *t_mm_node;
1133: int skip;
1134: int line_number = 0;
1135:
1136:
1137: t_mm_node = mm_list;
1138: skip = mm_text_offset_target >> 4;
1139:
1140: // first skip however lines mm_text_offset_target tells us to
1141: // note: this is for negative skip, positive skip is handled below
1142: while (skip < 0) {
1143: line_number++;
1144: skip++;
1145: }
1146:
1147: // iterate over the linked list of entry nodes, possibly rendering
1148: while (t_mm_node != NULL) {
1149: if (skip > 0) {
1150: skip--;
1151: } else {
1152: if (line_number < (mm_dm_height_lines - 1)) {
1153: // only print nonblank lines as not to overwrite a wrapped line
1154: if (strncmp(t_mm_node->label, "", 1) != 0) {
1155: printf("\x1b[%02d;0H %s",
1156: line_number,
1157: t_mm_node->label);
1158: }
1159: line_number++;
1160: }
1161: }
1162: t_mm_node = t_mm_node->next;
1163: }
1164:
1165: }
1166:
1167:
1168: void mm_hist_push(const char *label) {
1169:
1170: gzmcp_menu_node *new_node;
1171:
1172: // allocate memory for a new node
1173: new_node = malloc(sizeof(gzmcp_menu_node));
1174: if (new_node == NULL) {
1175: die();
1176: }
1177:
1178: strncpy(new_node->label,
1179: label,
1180: MENU_LABEL_MAXLEN);
1181: // not used
1182: new_node->select_cb = NULL;
1183: // not used
1184: new_node->selectable = 0;
1185: new_node->next = mm_hist;
1186:
1187: mm_hist = new_node;
1188:
1189: }
1190:
1191:
1192: char *mm_hist_peek(void) {
1193: if (mm_hist == NULL) return NULL;
1194: return (mm_hist->label);
1195: }
1196:
1197:
1198: void mm_hist_pop(void) {
1199:
1200: gzmcp_menu_node *t_mm_node;
1201:
1202: if (mm_hist == NULL) return;
1203:
1204: t_mm_node = mm_hist->next;
1205: free(mm_hist);
1206: mm_hist = t_mm_node;
1207:
1208: }
1209:
1210: void mm_go_whammypad(void) {
1211:
1212: system_xmode_new(MODE_TPW__JAM);
1213:
1214: }
1215:
1216:
1217: void mm_go_settings(void) {
1218:
1219: // XXX: unimplemented
1220:
1221: }
1222:
1223:
1224: void mm_go_files(void) {
1225:
1226: mcp_snd_click();
1227: mm_hist_push("(go back)");
1228: mcp_fade_start(MCP_SUB_SCREEN,
1229: 3,
1230: 0,
1231: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1232: idle_rate,
1233: &mm_create_files);
1234:
1235: }
1236:
1237:
1238: void mm_go_usermanual(void) {
1239:
1240: // XXX: unimplemented
1241:
1242: }
1243:
1244:
1245: void mm_go_misc(void) {
1246:
1247: mcp_snd_click();
1248: mm_hist_push("(go back)");
1249: mcp_fade_start(MCP_SUB_SCREEN,
1250: 3,
1251: 0,
1252: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1253: idle_rate,
1254: &mm_create_misc);
1255:
1256: }
1257:
1258:
1259: void mm_go_shutdown(void) {
1260:
1261: // this isn't enough, netsearching seems to suggest it isn't possible,
1262: // or at least widely known how to accomplish this.
1263:
1264: // this gets to sleep mode, but thats it
1265: netmode_go_offline();
1266: powerOff(POWER_ALL | PM_SYSTEM_PWR);
1267: swiSetHaltCR(0x80);
1268:
1269: }
1270:
1271:
1272: void mm_go_get_update(void) {
1273:
1274: system_xmode_new(MODE_GET_UPDATE);
1275:
1276: }
1277:
1278:
1279: void mm_go_get_and_burn_update(void) {
1280:
1281: overwrite_self_with_update = 1;
1282: system_xmode_new(MODE_GET_UPDATE);
1283:
1284: }
1285:
1286:
1287: void mm_go_ssid_input(void) {
1288:
1289: mm_hist->select_cb = &mm_create_misc;
1290: system_xmode_new(MODE_SSID__INPUT);
1291:
1292: }
1293:
1294:
1295: void mm_go_show_credits(void) {
1296:
1297: mm_hist->select_cb = &mm_create_misc;
1298: system_xmode_new(MODE_INTRO__CREDITS);
1299:
1300: }
1301:
1302:
1303: void mm_go_show_ssid_list(void) {
1304:
1305: Wifi_AccessPoint ap;
1306: char ssidbuf[MENU_LABEL_MAXLEN + 1];
1307: int i, nn;
1308:
1309: mm_free();
1310:
1311: mm_set_doublemode(0);
1312:
1313: mm_add_node("/========================\\", 0, NULL);
1314: mm_add_node("| |", 0, NULL);
1315: mm_add_node("| Scanned WiFi Networks |", 0, NULL);
1316: mm_add_node("| |", 0, NULL);
1317: mm_add_node("\\========================/", 0, NULL);
1318: mm_add_node("", 0, NULL);
1319: mm_add_node("(go back)",
1320: 1,
1321: mm_go_back);
1322: mm_add_node("", 0, NULL);
1323:
1324: nn = Wifi_GetNumAP();
1325:
1326: for (i = 0 ; i < nn ; i++) {
1327:
1328: if (WIFI_RETURN_OK == Wifi_GetAPData(i,&ap)) {
1329:
1330: if (strncmp(ap.ssid, "", 2) != 0) {
1331:
1332: if (mm_get_index_by_label(ap.ssid) == -1) {
1333:
1334: strncpy(ssidbuf, ap.ssid, MENU_LABEL_MAXLEN);
1335:
1336: mm_add_node("", 0, NULL);
1337: mm_add_node(ssidbuf,
1338: 1,
1339: NULL);
1340:
1341: } // end if not already in the list
1342:
1343: } // end if not an empty string
1344:
1345: } // end got data for ap
1346:
1347: } // end iteration over networks
1348:
1349: mm_alphasort(mm_list, 9, num_selections);
1350:
1351: mm_set_selection_by_hist();
1352:
1353: mcp_fade_start(MCP_SUB_SCREEN,
1354: 3,
1355: 1,
1356: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1357: idle_rate,
1358: NULL);
1359:
1360: }
1361:
1362:
1363: void mm_go_back(void) {
1364:
1365: mcp_snd_click();
1366: mcp_fade_start(MCP_SUB_SCREEN,
1367: 3,
1368: 0,
1369: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1370: idle_rate,
1371: &mm_create_last);
1372:
1373: }
1374:
1375:
1376: void mm_go_misc_back(void) {
1377:
1378: mcp_snd_click();
1379: mcp_fade_start(MCP_SUB_SCREEN,
1380: 3,
1381: 0,
1382: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1383: idle_rate,
1384: &mm_create_root);
1385:
1386: }
1387:
1388:
1389: void mm_go_files_back(void) {
1390:
1391: // note: this made a big difference, I.e. I think was causing
1392: // stack corruption of the 16k stack
1393: // char tmpstring_dir[FS_PATH_MAXLEN];
1394: // char tmpstring_base[FS_PATH_MAXLEN];
1395: char *tmpstring_dir;
1396:
1397:
1398: if ((tmpstring_dir = malloc(FS_PATH_MAXLEN)) == NULL ) die();
1399:
1400: if (strncmp(pwd, "/", FS_PATH_MAXLEN) == 0) {
1401: // if pwd is /, go back
1402: mcp_snd_click();
1403: mcp_fade_start(MCP_SUB_SCREEN,
1404: 3,
1405: 0,
1406: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1407: idle_rate,
1408: &mm_create_root);
1409: } else {
1410: // else cd .. and regenerate list
1411: // note: dirname manpage on linux.com, but not f10, says not to free
1412: // the returned pointer. Also warnings about passed string being
1413: // modified.
1414: // strncpy(tmpstring_dir, dirname(pwd), FS_PATH_MAXLEN);
1415: // ... but my dirname implementation is different at the moment
1416: mcp_dirname(tmpstring_dir, pwd);
1417:
1418: strncpy(pwd, tmpstring_dir, FS_PATH_MAXLEN);
1419:
1420: mcp_fade_start(MCP_SUB_SCREEN,
1421: 3,
1422: 0,
1423: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1424: idle_rate,
1425: &mm_create_files);
1426: }
1427:
1428: free(tmpstring_dir);
1429:
1430: return;
1431: }
1432:
1433:
1434: void mm_go_files_entry(void) {
1435:
1436: // stack->heap fodder
1437: struct stat t_statbuf;
1438:
1439: // use selection_index to retrieve entry string/filename
1440: // and then use pwd to craft full path
1441: if (strncmp(pwd, "/", 2) == 0) {
1442: snprintf(t_scratch_path,
1443: FS_PATH_MAXLEN,
1444: "/%s",
1445: (mm_get_node_by_index(selection_index))->label);
1446: } else {
1447: snprintf(t_scratch_path,
1448: FS_PATH_MAXLEN,
1449: "%s/%s",
1450: pwd,
1451: (mm_get_node_by_index(selection_index))->label);
1452: }
1453:
1454: // TODO: catch error
1455: stat(t_scratch_path, &t_statbuf);
1456:
1457: if (S_ISDIR(t_statbuf.st_mode)) {
1458: // set new pwd
1459: strncpy(pwd, t_scratch_path, FS_PATH_MAXLEN);
1460: // lose a trailing slash if present
1461: // note: I really think I tried the same with t_scratch_path above,
1462: // and it didn't make its way through to pwd??
1463: if (pwd[strlen(pwd) - 1] == '/') {
1464: pwd[strlen(pwd) - 1] = '\0';
1465: }
1466: mm_hist_push("../ (go back)");
1467: mcp_snd_click();
1468: mcp_fade_start(MCP_SUB_SCREEN,
1469: 3,
1470: 0,
1471: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1472: idle_rate,
1473: &mm_create_files);
1474: } else {
1475: execz(t_scratch_path, 0, NULL);
1476: }
1477:
1478: // TODO: add handlers for ogg, preverification of valid .nds, etc...
1479:
1480: }
1481:
1482:
1483: void mm_create(void (*create_func)(void)) {
1484:
1485: if (mm_hist != NULL) {
1486: mm_hist->select_cb = create_func;
1487: }
1488:
1489: (*create_func)();
1490:
1491: }
1492:
1493: void mm_create_last(void) {
1494:
1495: if (mm_hist != NULL) {
1496: if (mm_hist->select_cb != NULL) {
1497: (*(mm_hist->select_cb))();
1498: return;
1499: } else if (mm_hist->next != NULL) {
1500: if (mm_hist->next->select_cb != NULL) {
1501: (*(mm_hist->next->select_cb))();
1502: return;
1503: }
1504: }
1505: }
1506:
1507: // fallback to root
1508: mm_create_root();
1509: }
1510:
1511:
1512: void mm_create_root(void) {
1513:
1514: mm_free();
1515:
1516: mm_set_doublemode(1);
1517:
1518: mm_add_node("WhammyPad!",
1519: 1,
1520: mm_go_whammypad);
1521: mm_add_node("", 0, NULL);
1522: mm_add_node(" settings",
1523: 1,
1524: mm_go_settings);
1525: mm_add_node("", 0, NULL);
1526: mm_add_node(" help",
1527: 1,
1528: mm_go_usermanual);
1529: mm_add_node("", 0, NULL);
1530: mm_add_node(" advanced",
1531: 1,
1532: mm_go_misc);
1533: mm_add_node("", 0, NULL);
1534: mm_add_node("filesystem",
1535: 1,
1536: mm_go_files);
1537:
1538: mm_set_selection_by_hist();
1539:
1540: mcp_fade_start(MCP_SUB_SCREEN,
1541: 3,
1542: 1,
1543: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1544: idle_rate,
1545: NULL);
1546:
1547: }
1548:
1549:
1550: void mm_create_misc(void) {
1551:
1552: mm_free();
1553:
1554: mm_set_doublemode(0);
1555:
1556: mm_add_node("/========================\\", 0, NULL);
1557: mm_add_node("| |", 0, NULL);
1558: mm_add_node("| MCP:: Advanced Options |", 0, NULL);
1559: mm_add_node("| |", 0, NULL);
1560: mm_add_node("\\========================/", 0, NULL);
1561: mm_add_node("", 0, NULL);
1562: mm_add_node("(go back)",
1563: 1,
1564: mm_go_misc_back);
1565: mm_add_node("", 0, NULL);
1566: mm_add_node("get update",
1567: 1,
1568: mm_go_get_update);
1569: mm_add_node("", 0, NULL);
1570: mm_add_node("get and burn-in update",
1571: 1,
1572: mm_go_get_and_burn_update);
1573: mm_add_node("", 0, NULL);
1574: mm_add_node("show credits",
1575: 1,
1576: mm_go_show_credits);
1577: mm_add_node("", 0, NULL);
1578: mm_add_node("set custom ssid",
1579: 1,
1580: mm_go_ssid_input);
1581: mm_add_node("", 0, NULL);
1582: mm_add_node("show ssid list",
1583: 1,
1584: mm_go_show_ssid_list);
1585:
1586: /*
1587: // debugging
1588: char label[16];
1589: int i;
1590: for (i = 3; i <= 46; i++) {
1591: mm_add_node("", 0, NULL);
1592: snprintf(label, 16, "adv-test-%02d", i);
1593: mm_add_node(label,
1594: i % 2,
1595: NULL);
1596: }
1597: mm_alphasort(mm_list, 8, num_selections);
1598: // /debugging
1599: */
1600:
1601: mm_set_selection_by_hist();
1602:
1603: mcp_fade_start(MCP_SUB_SCREEN,
1604: 3,
1605: 1,
1606: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1607: idle_rate,
1608: NULL);
1609: }
1610:
1611:
1612: void mm_create_files(void) {
1613:
1614: // the directory to open and scan (will be whatever pwd[] is)
1615: DIR *dir;
1616: // note: if the 16k stack space becomes really tight, this is a prime
1617: // candidate for something to malloc on the heap
1618: // TODO: detect directory and add trailing / to label
1619: // - and hope further trailing / handline is unneeded
1620: // struct stat t_statbuf;
1621: // hint: 42
1622: struct dirent *arthur;
1623:
1624: // stack->heap fodder
1625: struct stat t_statbuf;
1626:
1627:
1628: mm_free();
1629:
1630: mm_set_doublemode(0);
1631:
1632: mm_add_node("/========================\\", 0, NULL);
1633: mm_add_node("| |", 0, NULL);
1634: mm_add_node("| Contents of directory- |", 0, NULL);
1635: mm_add_node("| |", 0, NULL);
1636: if (strncmp(pwd, "/", 2) == 0) {
1637: snprintf(t_scratch_path,
1638: MENU_LABEL_MAXLEN,
1639: "| / (root) |");
1640:
1641: } else {
1642: snprintf(t_scratch_path,
1643: MENU_LABEL_MAXLEN,
1644: "| %-23s|",
1645: pwd);
1646: }
1647: mm_add_node(t_scratch_path,
1648: 0,
1649: NULL);
1650: // allow wrappage
1651: mm_add_node("| |", 0, NULL);
1652: mm_add_node("\\========================/", 0, NULL);
1653: mm_add_node("", 0, NULL);
1654:
1655: if (strncmp(pwd, "/", 2) == 0) {
1656: mm_add_node("(go back)",
1657: 1,
1658: mm_go_files_back);
1659: } else {
1660: mm_add_node("../ (go back)",
1661: 1,
1662: mm_go_files_back);
1663: }
1664:
1665: // open directory
1666: dir = opendir(pwd);
1667:
1668: // catch possible error
1669: if (dir == NULL) {
1670: mm_add_node("", 0, NULL);
1671: mm_add_node("ERROR: opendir failed",
1672: 0,
1673: NULL);
1674:
1675: mm_set_selection_by_index(1);
1676:
1677: return;
1678: }
1679:
1680: // readdir returns struct dirent *, which has d_name[NAME_MAX] entry name string, and NULL on end of dir
1681: while ((arthur = readdir(dir)) != NULL) {
1682: // leave out . and .. (for .. we have back already)
1683: if ((strncmp(arthur->d_name, ".", 2) != 0) &&
1684: (strncmp(arthur->d_name, "..", 3) != 0)) {
1685:
1686: if (strncmp(pwd, "/", 2) == 0) {
1687: snprintf(t_scratch_path,
1688: FS_PATH_MAXLEN,
1689: "/%s",
1690: arthur->d_name);
1691: } else {
1692: snprintf(t_scratch_path,
1693: FS_PATH_MAXLEN,
1694: "%s/%s",
1695: pwd,
1696: arthur->d_name);
1697: }
1698: // TODO: catch error
1699: retval = stat(t_scratch_path, &t_statbuf);
1700:
1701: if (retval == -1) {
1702: // die("stat error");
1703: die();
1704: } else if (S_ISDIR(t_statbuf.st_mode)) {
1705: snprintf(t_scratch_path,
1706: MENU_LABEL_MAXLEN,
1707: "%s/",
1708: arthur->d_name);
1709: } else {
1710: snprintf(t_scratch_path,
1711: MENU_LABEL_MAXLEN,
1712: "%s",
1713: arthur->d_name);
1714: }
1715:
1716: mm_add_node("", 0, NULL);
1717: mm_add_node(t_scratch_path,
1718: 1,
1719: mm_go_files_entry);
1720: }
1721: }
1722:
1723: // TODO: catch error / rv=-1
1724: closedir(dir);
1725:
1726: // 9 is dependent on header formatting above (and doublespacing)
1727: mm_alphasort(mm_list, 9, num_selections);
1728:
1729: mm_set_selection_by_label(mm_hist_peek());
1730: mm_hist_pop();
1731:
1732: mcp_fade_start(MCP_SUB_SCREEN,
1733: 3,
1734: 1,
1735: MAIN_MENU__BOT_TXT_INTRAMODE_FADE_DURATION_MS,
1736: idle_rate,
1737: NULL);
1738: }
1739: