Chameleon

Chameleon Svn Source Tree

Root/trunk/i386/boot2/options.c

1/*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999-2004 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 2.0 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#include "boot.h"
26#include "bootstruct.h"
27#include "fdisk.h"
28#include "ramdisk.h"
29#include "gui.h"
30#include "term.h"
31#include "embedded.h"
32#include "pci.h"
33#include "modules.h"
34
35#if DEBUG
36#define DBG(x...)printf(x)
37#else
38#define DBG(x...)msglog(x)
39#endif
40
41bool showBootBanner = true; //Azi:showinfo
42static bool shouldboot = false;
43
44extern int multiboot_timeout;
45extern int multiboot_timeout_set;
46
47extern BVRef bvChain;
48//extern intmenucount;
49
50extern intgDeviceCount;
51
52intselectIndex = 0;
53MenuItem * menuItems = NULL;
54
55enum {
56 kMenuTopRow = 5,
57 kMenuMaxItems = 10,
58 kScreenLastRow = 24
59};
60
61extern char* msgbuf;
62
63void showTextBuffer(char *buf_orig, int size);
64
65//==========================================================================
66
67typedef struct {
68 int x;
69 int y;
70 int type;
71} CursorState;
72
73static void changeCursor( int col, int row, int type, CursorState * cs )
74{
75 if (cs) getCursorPositionAndType( &cs->x, &cs->y, &cs->type );
76 setCursorType( type );
77 setCursorPosition( col, row, 0 );
78}
79
80static void moveCursor( int col, int row )
81{
82 setCursorPosition( col, row, 0 );
83}
84
85static void restoreCursor( const CursorState * cs )
86{
87 setCursorPosition( cs->x, cs->y, 0 );
88 setCursorType( cs->type );
89}
90
91//==========================================================================
92
93/* Flush keyboard buffer; returns TRUE if any of the flushed
94 * characters was F8.
95 */
96
97static bool flushKeyboardBuffer(void)
98{
99 bool status = false;
100
101 while ( readKeyboardStatus() ) {
102 if (bgetc() == 0x4200) status = true;
103 }
104 return status;
105}
106
107//==========================================================================
108
109static int countdown( const char * msg, int row, int timeout )
110{
111 unsigned long time;
112 int ch = 0;
113 int col = strlen(msg) + 1;
114
115 flushKeyboardBuffer();
116
117if( bootArgs->Video.v_display == VGA_TEXT_MODE )
118{
119moveCursor( 0, row );
120printf(msg);
121
122} else {
123
124position_t p = pos( gui.screen.width / 2 + 1 , ( gui.devicelist.pos.y + 3 ) + ( ( gui.devicelist.height - gui.devicelist.iconspacing ) / 2 ) );
125
126char dummy[80];
127getBootVolumeDescription( gBootVolume, dummy, sizeof(dummy) - 1, true );
128drawDeviceIcon( gBootVolume, gui.screen.pixmap, p, true );
129drawStrCenteredAt( (char *) msg, &font_small, gui.screen.pixmap, gui.countdown.pos );
130
131// make this screen the new background
132memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
133
134}
135
136int multi_buff = 18 * (timeout);
137 int multi = ++multi_buff;
138
139 int lasttime=0;
140
141 for ( time = time18(), timeout++; timeout > 0; )
142 {
143if( time18() > lasttime)
144{
145multi--;
146lasttime=time18();
147}
148
149 if ( (ch = readKeyboardStatus()) )
150 break;
151
152 // Count can be interrupted by holding down shift,
153 // control or alt key
154 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 )
155{
156 ch = 1;
157 break;
158 }
159
160 if ( time18() >= time )
161 {
162 time += 18;
163 timeout--;
164
165if( bootArgs->Video.v_display == VGA_TEXT_MODE )
166{
167moveCursor( col, row );
168printf("(%d) ", timeout);
169}
170 }
171
172if( bootArgs->Video.v_display != VGA_TEXT_MODE )
173{
174drawProgressBar( gui.screen.pixmap, 100, gui.progressbar.pos , ( multi * 100 / multi_buff ) );
175gui.redraw = true;
176updateVRAM();
177}
178
179 }
180
181 flushKeyboardBuffer();
182
183 return ch;
184}
185
186//==========================================================================
187
188char gBootArgs[BOOT_STRING_LEN];
189static char * gBootArgsPtr = gBootArgs;
190static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1;
191static char booterCommand[BOOT_STRING_LEN];
192static char booterParam[BOOT_STRING_LEN];
193
194static void clearBootArgs(void)
195{
196gBootArgsPtr = gBootArgs;
197memset(gBootArgs, '\0', BOOT_STRING_LEN);
198
199if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
200clearGraphicBootPrompt();
201}
202execute_hook("ClearArgs", NULL, NULL, NULL, NULL);
203}
204
205void addBootArg(const char * argStr)
206{
207if ( (gBootArgsPtr + strlen(argStr) + 1) < gBootArgsEnd)
208{
209if(gBootArgsPtr != gBootArgs) *gBootArgsPtr++ = ' ';
210strcat(gBootArgs, argStr);
211gBootArgsPtr += strlen(argStr);
212}
213}
214
215//==========================================================================
216
217static void showBootPrompt(int row, bool visible)
218{
219extern char bootPrompt[];
220extern char bootRescanPrompt[];
221
222if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
223changeCursor( 0, row, kCursorTypeUnderline, 0 );
224clearScreenRows( row, kScreenLastRow );
225}
226
227clearBootArgs();
228
229if (visible) {
230if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
231if (gEnableCDROMRescan) {
232printf( bootRescanPrompt );
233} else {
234printf( bootPrompt );
235printf( gBootArgs );
236}
237}
238} else {
239if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
240clearGraphicBootPrompt();
241} else {
242printf("Press Enter to start up the foreign OS. ");
243}
244}
245}
246
247//==========================================================================
248
249static void updateBootArgs( int key )
250{
251 key = ASCII_KEY(key);
252
253 switch ( key )
254 {
255 case KEY_BKSP:
256 if ( gBootArgsPtr > gBootArgs )
257 {
258 *--gBootArgsPtr = '\0';
259
260 int x, y, t;
261 getCursorPositionAndType( &x, &y, &t );
262 if ( x == 0 && y )
263 {
264 x = 80; y--;
265 }
266 if (x) {
267x--;
268}
269
270if( bootArgs->Video.v_display == VGA_TEXT_MODE )
271{
272setCursorPosition( x, y, 0 );
273putca(' ', 0x07, 1);
274}
275 else
276 {
277 updateGraphicBootPrompt();
278 }
279 }
280break;
281
282 default:
283 if ( key >= ' ' && gBootArgsPtr < gBootArgsEnd)
284 {
285 *gBootArgsPtr++ = key;
286
287 if( bootArgs->Video.v_display != VGA_TEXT_MODE ) updateGraphicBootPrompt();
288 else if ( key >= ' ' && key < 0x7f) putchar(key);
289}
290
291break;
292 }
293}
294
295//==========================================================================
296
297static const MenuItem * gMenuItems = NULL;
298
299static int gMenuItemCount;
300static int gMenuRow;
301static int gMenuHeight;
302static int gMenuTop;
303static int gMenuBottom;
304static int gMenuSelection;
305
306static int gMenuStart;
307static int gMenuEnd;
308
309static void printMenuItem( const MenuItem * item, int highlight )
310{
311 printf(" ");
312
313 if ( highlight )
314 putca(' ', 0x70, strlen(item->name) + 4);
315 else
316 putca(' ', 0x07, 40);
317
318 printf(" %40s\n", item->name);
319}
320
321//==========================================================================
322
323static void showMenu( const MenuItem * items, int count,
324 int selection, int row, int height )
325{
326 int i;
327 CursorState cursorState;
328
329 if ( items == NULL || count == 0 )
330return;
331
332 // head and tail points to the start and the end of the list.
333 // top and bottom points to the first and last visible items
334 // in the menu window.
335
336 gMenuItems= items;
337 gMenuRow= row;
338 gMenuHeight= height;
339 gMenuItemCount= count;
340 gMenuTop= 0;
341 gMenuBottom= MIN( count, height ) - 1;
342 gMenuSelection= selection;
343
344 gMenuStart= 0;
345 gMenuEnd = MIN( count, gui.maxdevices ) - 1;
346
347// If the selected item is not visible, shift the list down.
348
349 if ( gMenuSelection > gMenuBottom )
350 {
351 gMenuTop += ( gMenuSelection - gMenuBottom );
352 gMenuBottom = gMenuSelection;
353 }
354
355if ( gMenuSelection > gMenuEnd )
356 {
357gMenuStart += ( gMenuSelection - gMenuEnd );
358 gMenuEnd = gMenuSelection;
359 }
360
361// Draw the visible items.
362
363if( bootArgs->Video.v_display != VGA_TEXT_MODE )
364
365drawDeviceList(gMenuStart, gMenuEnd, gMenuSelection);
366
367else {
368
369changeCursor( 0, row, kCursorTypeHidden, &cursorState );
370
371for ( i = gMenuTop; i <= gMenuBottom; i++ )
372{
373printMenuItem( &items[i], (i == gMenuSelection) );
374}
375
376restoreCursor( &cursorState );
377 }
378}
379
380//==========================================================================
381
382static int updateMenu( int key, void ** paramPtr )
383{
384 int moved = 0;
385
386 union {
387 struct {
388 unsigned int
389 selectionUp : 1,
390 selectionDown : 1,
391 scrollUp : 1,
392 scrollDown : 1;
393 } f;
394 unsigned int w;
395 } draw = {{0}};
396
397 if ( gMenuItems == NULL )
398return 0;
399
400if( bootArgs->Video.v_display != VGA_TEXT_MODE )
401{
402int res;
403
404// set navigation keys for horizontal layout as defaults
405int previous= 0x4B00;// left arrow
406int subsequent= 0x4D00;// right arrow
407int menu= 0x5000;// down arrow
408
409if ( gui.layout == VerticalLayout )
410{
411// set navigation keys for vertical layout
412previous= 0x4800;// up arrow
413subsequent= 0x5000;// down arrow
414menu= 0x4B00;// right arrow
415}
416
417if ( key == previous )
418{
419if ( gMenuSelection > gMenuTop )
420draw.f.selectionUp = 1;
421else if ( gMenuTop > 0 )
422draw.f.scrollDown = 1;
423
424}
425
426else if ( key == subsequent )
427{
428if ( gMenuSelection != gMenuBottom)
429draw.f.selectionDown = 1;
430else if ( gMenuBottom < ( gMenuItemCount - 1 ) )
431draw.f.scrollUp = 1;
432}
433
434else if ( key == menu )
435{
436if ( gui.menu.draw )
437updateInfoMenu(key);
438else
439drawInfoMenu();
440}
441
442else if ( gui.menu.draw )
443{
444res = updateInfoMenu(key);
445
446if ( res == CLOSE_INFO_MENU )
447gui.menu.draw = false;
448else
449{
450shouldboot = ( res != DO_NOT_BOOT );
451
452if ( shouldboot )
453gui.menu.draw = false;
454
455switch (res)
456{
457case BOOT_NORMAL:
458gVerboseMode = false;
459gBootMode = kBootModeNormal;
460break;
461
462case BOOT_VERBOSE:
463gVerboseMode = true;
464gBootMode = kBootModeNormal;
465addBootArg(kVerboseModeFlag);
466break;
467
468case BOOT_IGNORECACHE:
469gVerboseMode = false;
470gBootMode = kBootModeNormal;
471addBootArg(kIgnoreCachesFlag);
472break;
473
474case BOOT_SINGLEUSER:
475gVerboseMode = true;
476gBootMode = kBootModeNormal;
477addBootArg(kSingleUserModeFlag);
478break;
479}
480
481}
482
483}
484
485} else {
486switch ( key )
487{
488 case 0x4800: // Up Arrow
489if ( gMenuSelection != gMenuTop )
490draw.f.selectionUp = 1;
491else if ( gMenuTop > 0 )
492draw.f.scrollDown = 1;
493break;
494
495case 0x5000: // Down Arrow
496if ( gMenuSelection != gMenuBottom )
497draw.f.selectionDown = 1;
498else if ( gMenuBottom < (gMenuItemCount - 1) )
499draw.f.scrollUp = 1;
500break;
501}
502}
503
504 if ( draw.w )
505 {
506 if ( draw.f.scrollUp )
507 {
508 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, 1);
509 gMenuTop++; gMenuBottom++;
510gMenuStart++; gMenuEnd++;
511 draw.f.selectionDown = 1;
512 }
513
514 if ( draw.f.scrollDown )
515 {
516 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, -1);
517 gMenuTop--; gMenuBottom--;
518 gMenuStart--; gMenuEnd--;
519 draw.f.selectionUp = 1;
520 }
521
522 if ( draw.f.selectionUp || draw.f.selectionDown )
523 {
524
525CursorState cursorState;
526
527// Set cursor at current position, and clear inverse video.
528
529if( bootArgs->Video.v_display == VGA_TEXT_MODE )
530{
531changeCursor( 0, gMenuRow + gMenuSelection - gMenuTop, kCursorTypeHidden, &cursorState );
532printMenuItem( &gMenuItems[gMenuSelection], 0 );
533}
534
535if ( draw.f.selectionUp )
536{
537gMenuSelection--;
538if(( gMenuSelection - gMenuStart) == -1 )
539{
540gMenuStart--;
541gMenuEnd--;
542}
543
544} else {
545gMenuSelection++;
546if(( gMenuSelection - ( gui.maxdevices - 1) - gMenuStart) > 0 )
547{
548gMenuStart++;
549gMenuEnd++;
550}
551 }
552
553if( bootArgs->Video.v_display == VGA_TEXT_MODE )
554 {
555moveCursor( 0, gMenuRow + gMenuSelection - gMenuTop );
556printMenuItem( &gMenuItems[gMenuSelection], 1 );
557restoreCursor( &cursorState );
558
559 } else
560
561drawDeviceList (gMenuStart, gMenuEnd, gMenuSelection);
562
563}
564
565 *paramPtr = gMenuItems[gMenuSelection].param;
566 moved = 1;
567 }
568
569return moved;
570}
571
572//==========================================================================
573
574static void skipblanks( const char ** cpp )
575{
576 while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp);
577}
578
579//==========================================================================
580
581static const char * extractKernelName( char ** cpp )
582{
583 char * kn = *cpp;
584 char * cp = *cpp;
585 char c;
586
587 // Convert char to lower case.
588
589 c = *cp | 0x20;
590
591 // Must start with a letter or a '/'.
592
593 if ( (c < 'a' || c > 'z') && ( c != '/' ) )
594 return 0;
595
596 // Keep consuming characters until we hit a separator.
597
598 while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') )
599 cp++;
600
601 // Only SPACE or TAB separator is accepted.
602 // Reject everything else.
603
604 if (*cp == '=')
605 return 0;
606
607 // Overwrite the separator, and move the pointer past
608 // the kernel name.
609
610 if (*cp != '\0') *cp++ = '\0';
611 *cpp = cp;
612
613 return kn;
614}
615
616//==========================================================================
617
618static void printMemoryInfo(void)
619{
620 int line;
621 int i;
622 MemoryRange *mp = bootInfo->memoryMap;
623
624 // Activate and clear page 1
625 setActiveDisplayPage(1);
626 clearScreenRows(0, 24);
627 setCursorPosition( 0, 0, 1 );
628
629 printf("BIOS reported memory ranges:\n");
630 line = 1;
631 for (i=0; i<bootInfo->memoryMapCount; i++) {
632 printf("Base 0x%08x%08x, ",
633 (unsigned long)(mp->base >> 32),
634 (unsigned long)(mp->base));
635 printf("length 0x%08x%08x, type %d\n",
636 (unsigned long)(mp->length >> 32),
637 (unsigned long)(mp->length),
638 mp->type);
639 if (line++ > 20) {
640 pause();
641 line = 0;
642 }
643 mp++;
644 }
645 if (line > 0) {
646 pause();
647 }
648
649 setActiveDisplayPage(0);
650}
651
652char *getMemoryInfoString()
653{
654int i, bufflen;
655MemoryRange *mp = bootInfo->memoryMap;
656char *buff = malloc(sizeof(char)*1024);
657if(!buff) {
658return 0;
659}
660
661static const char info[] = "BIOS reported memory ranges:\n";
662bufflen = sprintf(buff, "%s", info);
663
664for (i = 0;
665(i < bootInfo->memoryMapCount) && (bufflen < 1024); /* prevent buffer overflow */
666i++) {
667bufflen += snprintf(buff+bufflen, 1024-bufflen, "Base 0x%08x%08x, ",
668 (unsigned long)(mp->base >> 32),
669 (unsigned long)(mp->base));
670bufflen += snprintf(buff+bufflen, 1024-bufflen, "length 0x%08x%08x, type %d\n",
671 (unsigned long)(mp->length >> 32),
672 (unsigned long)(mp->length),
673 mp->type);
674mp++;
675}
676return buff;
677}
678
679//==========================================================================
680
681void lspci(void)
682{
683if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
684setActiveDisplayPage(1);
685clearScreenRows(0, 24);
686setCursorPosition(0, 0, 1);
687}
688
689dump_pci_dt(root_pci_dev->children);
690
691pause();
692
693if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
694setActiveDisplayPage(0);
695}
696}
697
698//==========================================================================
699
700int getBootOptions(bool firstRun)
701{
702int i;
703int key;
704int nextRow;
705int timeout;
706int bvCount;
707BVRef bvr;
708BVRef menuBVR;
709bool showPrompt, newShowPrompt, isCDROM;
710
711// Initialize default menu selection entry.
712gBootVolume = menuBVR = selectBootVolume(bvChain);
713
714if (biosDevIsCDROM(gBIOSDev)) {
715isCDROM = true;
716} else {
717isCDROM = false;
718}
719
720// ensure we're in graphics mode if gui is setup
721if (firstRun && gui.initialised && bootArgs->Video.v_display == VGA_TEXT_MODE)
722{
723setVideoMode(GRAPHICS_MODE, 0);
724}
725
726// Clear command line boot arguments
727clearBootArgs();
728
729// Allow user to override default timeout.
730if (multiboot_timeout_set) {
731timeout = multiboot_timeout;
732} else if (!getIntForKey(kTimeoutKey, &timeout, &bootInfo->chameleonConfig)) {
733/* If there is no timeout key in the file use the default timeout
734 which is different for CDs vs. hard disks. However, if not booting
735 a CD and no config file could be loaded set the timeout
736 to zero which causes the menu to display immediately.
737 This way, if no partitions can be found, that is the disk is unpartitioned
738 or simply cannot be read) then an empty menu is displayed.
739 If some partitions are found, for example a Windows partition, then
740 these will be displayed in the menu as foreign partitions.
741 */
742if (isCDROM) {
743timeout = kCDBootTimeout;
744} else {
745timeout = sysConfigValid ? kBootTimeout : 0;
746}
747}
748
749if (timeout < 0) {
750gBootMode |= kBootModeQuiet;
751}
752
753// If the user is holding down a modifier key, enter safe mode.
754if ((readKeyboardShiftFlags() & 0x0F) != 0) {
755gBootMode |= kBootModeSafe;
756}
757
758// Checking user pressed keys
759bool f8press = false, spress = false, vpress = false;
760while (readKeyboardStatus()) {
761key = bgetc ();
762if (key == 0x4200) f8press = true;
763if ((key & 0xff) == 's' || (key & 0xff) == 'S') spress = true;
764if ((key & 0xff) == 'v' || (key & 0xff) == 'V') vpress = true;
765}
766// If user typed F8, abort quiet mode, and display the menu.
767if (f8press) {
768gBootMode &= ~kBootModeQuiet;
769timeout = 0;
770}
771// If user typed 'v' or 'V', boot in verbose mode.
772if ((gBootMode & kBootModeQuiet) && firstRun && vpress) {
773addBootArg(kVerboseModeFlag);
774}
775// If user typed 's' or 'S', boot in single user mode.
776if ((gBootMode & kBootModeQuiet) && firstRun && spress) {
777addBootArg(kSingleUserModeFlag);
778}
779
780if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
781setCursorPosition(0, 0, 0);
782clearScreenRows(0, kScreenLastRow);
783if (!(gBootMode & kBootModeQuiet)) {
784// Display banner and show hardware info.
785printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
786printf(getVBEInfoString());
787}
788changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
789verbose("Scanning device %x...", gBIOSDev);
790}
791
792// When booting from CD, default to hard drive boot when possible.
793if (isCDROM && firstRun) {
794const char *val;
795char *prompt = NULL;
796char *name = NULL;
797int cnt;
798int optionKey;
799
800if (getValueForKey(kCDROMPromptKey, &val, &cnt, &bootInfo->chameleonConfig)) {
801prompt = malloc(cnt + 1);
802strncat(prompt, val, cnt);
803} else {
804name = malloc(80);
805getBootVolumeDescription(gBootVolume, name, 79, false);
806prompt = malloc(256);
807sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name);
808free(name);
809}
810
811if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->chameleonConfig )) {
812// The key specified is a special key.
813} else {
814// Default to F8.
815optionKey = 0x4200;
816}
817
818// If the timeout is zero then it must have been set above due to the
819// early catch of F8 which means the user wants to set boot options
820// which we ought to interpret as meaning he wants to boot the CD.
821if (timeout != 0) {
822key = countdown(prompt, kMenuTopRow, timeout);
823} else {
824key = optionKey;
825}
826
827if (prompt != NULL) {
828free(prompt);
829}
830
831clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
832
833// Hit the option key ?
834if (key == optionKey) {
835gBootMode &= ~kBootModeQuiet;
836timeout = 0;
837} else {
838key = key & 0xFF;
839
840// Try booting hard disk if user pressed 'h'
841if (biosDevIsCDROM(gBIOSDev) && key == 'h') {
842BVRef bvr;
843
844// Look at partitions hosting OS X other than the CD-ROM
845for (bvr = bvChain; bvr; bvr=bvr->next) {
846if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev) {
847gBootVolume = bvr;
848}
849}
850}
851goto done;
852}
853}
854
855if (gBootMode & kBootModeQuiet) {
856// No input allowed from user.
857goto done;
858}
859
860if (firstRun && timeout > 0 && countdown("Press any key to enter startup options.", kMenuTopRow, timeout) == 0) {
861// If the user is holding down a modifier key,
862// enter safe mode.
863if ((readKeyboardShiftFlags() & 0x0F) != 0) {
864gBootMode |= kBootModeSafe;
865}
866goto done;
867}
868
869if (gDeviceCount > 0) {
870// Allocate memory for an array of menu items.
871menuItems = malloc(sizeof(MenuItem) * gDeviceCount);
872if (menuItems == NULL) {
873goto done;
874}
875
876// Associate a menu item for each BVRef.
877for (bvr=bvChain, i=gDeviceCount-1, selectIndex=-1; bvr; bvr=bvr->next) {
878if (bvr->visible) {
879getBootVolumeDescription(bvr, menuItems[i].name, sizeof(menuItems[i].name) - 1, true);
880menuItems[i].param = (void *) bvr;
881if (bvr == menuBVR) {
882selectIndex = i;
883}
884i--;
885}
886}
887// Jief : In case the default partition (returned by selectBootVolume) is not in the menu
888if ( selectIndex == -1 )
889{
890selectIndex = 0;
891
892// gDeviceCount is actually > 0, so menuItems[selectIndex] exists
893menuBVR = (BVRef)(menuItems[selectIndex].param);
894// what happen is bvChain is empty ?
895}
896}
897
898if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
899// redraw the background buffer
900gui.logo.draw = true;
901drawBackground();
902gui.devicelist.draw = true;
903gui.redraw = true;
904if (!(gBootMode & kBootModeQuiet)) {
905
906// Check if "Boot Banner"=N switch is present in config file.
907getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->chameleonConfig);
908if (showBootBanner) {
909// Display banner and show hardware info.
910gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024);
911}
912
913// redraw background
914memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4);
915}
916} else {
917// Clear screen and hide the blinking cursor.
918clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
919changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
920}
921
922nextRow = kMenuTopRow;
923showPrompt = true;
924
925if (gDeviceCount) {
926if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
927printf("Use \30\31 keys to select the startup volume.");
928}
929showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
930nextRow += MIN( gDeviceCount, kMenuMaxItems ) + 3;
931}
932
933// Show the boot prompt.
934showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
935showBootPrompt( nextRow, showPrompt );
936
937do {
938if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
939// redraw background
940memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
941// reset cursor co-ords
942gui.debug.cursor = pos( gui.screen.width - 160 , 10 );
943}
944key = getchar();
945updateMenu( key, (void **) &menuBVR );
946newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
947
948if (newShowPrompt != showPrompt) {
949showPrompt = newShowPrompt;
950showBootPrompt( nextRow, showPrompt );
951}
952
953if (showPrompt) {
954updateBootArgs(key);
955}
956
957switch (key) {
958case KEY_ENTER:
959if (gui.menu.draw) {
960key=0;
961break;
962}
963if (*gBootArgs == '?') {
964char * argPtr = gBootArgs;
965
966// Skip the leading "?" character.
967argPtr++;
968getNextArg(&argPtr, booterCommand);
969getNextArg(&argPtr, booterParam);
970
971/*
972* TODO: this needs to be refactored.
973*/
974if (strcmp( booterCommand, "video" ) == 0) {
975if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
976showInfoBox(getVBEInfoString(), getVBEModeInfoString());
977} else {
978printVBEModeInfo();
979}
980} else if ( strcmp( booterCommand, "memory" ) == 0) {
981if (bootArgs->Video.v_display != VGA_TEXT_MODE ) {
982showInfoBox("Memory Map", getMemoryInfoString());
983} else {
984printMemoryInfo();
985}
986} else if (strcmp(booterCommand, "lspci") == 0) {
987lspci();
988} else if (strcmp(booterCommand, "log") == 0) {
989 showTextBuffer(msgbuf, strlen(msgbuf));
990} else if (strcmp(booterCommand, "more") == 0) {
991showTextFile(booterParam);
992} else if (strcmp(booterCommand, "rd") == 0) {
993processRAMDiskCommand(&argPtr, booterParam);
994} else if (strcmp(booterCommand, "norescan") == 0) {
995if (gEnableCDROMRescan) {
996gEnableCDROMRescan = false;
997break;
998}
999} else {
1000showHelp();
1001}
1002key = 0;
1003showBootPrompt(nextRow, showPrompt);
1004break;
1005}
1006gBootVolume = menuBVR;
1007setRootVolume(menuBVR);
1008gBIOSDev = menuBVR->biosdev;
1009break;
1010
1011case KEY_ESC:
1012clearBootArgs();
1013break;
1014
1015case KEY_F5:
1016// New behavior:
1017// Clear gBootVolume to restart the loop
1018// if the user enabled rescanning the optical drive.
1019// Otherwise boot the default boot volume.
1020if (gEnableCDROMRescan) {
1021gBootVolume = NULL;
1022clearBootArgs();
1023}
1024break;
1025
1026case KEY_F10:
1027gScanSingleDrive = false;
1028scanDisks(gBIOSDev, &bvCount);
1029gBootVolume = NULL;
1030clearBootArgs();
1031break;
1032
1033case KEY_TAB:
1034// New behavior:
1035// Switch between text & graphic interfaces
1036// Only Permitted if started in graphics interface
1037if (useGUI) {
1038if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1039setVideoMode(VGA_TEXT_MODE, 0);
1040
1041setCursorPosition(0, 0, 0);
1042clearScreenRows(0, kScreenLastRow);
1043
1044// Display banner and show hardware info.
1045printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1046printf(getVBEInfoString());
1047
1048clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1049changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1050
1051nextRow = kMenuTopRow;
1052showPrompt = true;
1053
1054if (gDeviceCount) {
1055printf("Use \30\31 keys to select the startup volume.");
1056showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1057nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3;
1058}
1059
1060showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1061showBootPrompt(nextRow, showPrompt);
1062//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1063} else {
1064gui.redraw = true;
1065setVideoMode(GRAPHICS_MODE, 0);
1066updateVRAM();
1067 updateGraphicBootPrompt();
1068}
1069}
1070key = 0;
1071break;
1072
1073default:
1074key = 0;
1075break;
1076}
1077} while (0 == key);
1078
1079done:
1080if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1081clearScreenRows(kMenuTopRow, kScreenLastRow);
1082changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1083}
1084shouldboot = false;
1085gui.menu.draw = false;
1086if (menuItems) {
1087free(menuItems);
1088menuItems = NULL;
1089}
1090execute_hook("BootOptions", gBootArgs, gBootArgsPtr, NULL, NULL);
1091return 0;
1092}
1093
1094//==========================================================================
1095
1096char gBootUUIDString[32+4+1] = ""; // UUID of the boot volume e.g. 5EB1869F-C4FA-3502-BDEB-3B8ED5D87292
1097extern unsigned char chainbootdev;
1098extern unsigned char chainbootflag;
1099
1100bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1101{
1102int argLen = argName ? strlen(argName) : 0;
1103int len = argLen + cnt + 1; // + 1 to account for space.
1104
1105if (argName)
1106{
1107len++; // +1 to account for '='
1108}
1109
1110if (len > *cntRemainingP) {
1111error("Warning: boot arguments too long, truncating\n");
1112return false;
1113}
1114
1115if (argName)
1116{
1117strncpy(*argP, argName, argLen);
1118*argP += argLen;
1119*argP[0] = '=';
1120(*argP)++;
1121}
1122
1123strncpy(*argP, val, cnt);
1124*argP += cnt;
1125*argP[0] = ' ';
1126(*argP)++;
1127*cntRemainingP -= len;
1128
1129return true;
1130}
1131
1132//
1133// Returns TRUE if an argument was copied, FALSE otherwise
1134bool
1135processBootArgument(
1136 const char *argName, // The argument to search for
1137 const char *userString, // Typed-in boot arguments
1138 const char *kernelFlags, // Kernel flags from config table
1139 const char *configTable,
1140 char **argP, // Output value
1141 int *cntRemainingP, // Output count
1142 char *foundVal, // found value
1143 int foundValSize // max found value size
1144 ) {
1145const char *val;
1146int cnt;
1147bool found = false;
1148
1149if (getValueForBootKey(userString, argName, &val, &cnt)) {
1150// Don't copy; these values will be copied at the end of argument processing.
1151found = true;
1152} else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1153// Don't copy; these values will be copied at the end of argument processing.
1154found = true;
1155} else if (getValueForKey(argName, &val, &cnt, &bootInfo->chameleonConfig)) {
1156copyArgument(argName, val, cnt, argP, cntRemainingP);
1157found = true;
1158}
1159if (found && foundVal) {
1160strlcpy(foundVal, val, foundValSize);
1161}
1162return found;
1163}
1164
1165// Maximum config table value size
1166#define VALUE_SIZE 2048
1167
1168int
1169processBootOptions()
1170{
1171const char *cp = gBootArgs;
1172const char *val = 0;
1173const char *kernel;
1174int cnt;
1175int userCnt;
1176int cntRemaining;
1177char *argP;
1178char *configKernelFlags;
1179char *valueBuffer;
1180
1181valueBuffer = malloc(VALUE_SIZE);
1182
1183skipblanks( &cp );
1184
1185// Update the unit and partition number.
1186
1187if ( gBootVolume ) {
1188if (!( gBootVolume->flags & kBVFlagNativeBoot )) {
1189readBootSector( gBootVolume->biosdev, gBootVolume->part_boff, (void *) 0x7c00 );
1190//
1191// Setup edx, and signal intention to chain load the
1192// foreign booter.
1193//
1194
1195chainbootdev = gBootVolume->biosdev;
1196chainbootflag = 1;
1197
1198return 1;
1199}
1200
1201setRootVolume(gBootVolume);
1202
1203}
1204// If no boot volume fail immediately because we're just going to fail
1205// trying to load the config file anyway.
1206else {
1207return -1;
1208}
1209
1210// Find out which version mac os we're booting.
1211strncpy(gMacOSVersion, gBootVolume->OSVersion, sizeof(gMacOSVersion));
1212
1213// Load config table specified by the user, or use the default.
1214
1215if (!getValueForBootKey(cp, "config", &val, &cnt)) {
1216val = 0;
1217cnt = 0;
1218}
1219
1220// Load com.apple.Boot.plist from the selected volume
1221// and use its contents to override default bootConfig.
1222
1223loadSystemConfig(&bootInfo->bootConfig);
1224loadChameleonConfig(&bootInfo->chameleonConfig, NULL);
1225
1226// Use the kernel name specified by the user, or fetch the name
1227// in the config table, or use the default if not specified.
1228// Specifying a kernel name on the command line, or specifying
1229// a non-default kernel name in the config file counts as
1230// overriding the kernel, which causes the kernelcache not
1231// to be used.
1232
1233gOverrideKernel = false;
1234if (( kernel = extractKernelName((char **)&cp) ))
1235{
1236strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
1237}
1238else
1239{
1240if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) )
1241{
1242strlcpy( bootInfo->bootFile, val, cnt+1 );
1243}
1244else
1245{
1246if( YOSEMITE ) // is 10.10
1247{
1248
1249strlcpy( bootInfo->bootFile, kOSXKernel, sizeof(bootInfo->bootFile) );
1250//printf(HEADER "/System/Library/Kernels/%s\n", bootInfo->bootFile);
1251}
1252else
1253{ // OSX is not 10.10
1254
1255strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
1256//printf(HEADER "/%s\n", bootInfo->bootFile);
1257}
1258}
1259}
1260
1261if (!YOSEMITE) // not 10.10 so 10.9 and previus
1262{
1263if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0)
1264{
1265 //printf(HEADER "org.chameleon.Boot.plist found path for custom '%s' found!\n", bootInfo->bootFile);
1266gOverrideKernel = true;
1267}
1268}
1269else
1270{ // OSX is 10.10
1271if (strcmp( bootInfo->bootFile, kOSXKernel ) != 0)
1272{
1273 //printf(HEADER "org.chameleon.Boot.plist found path for custom '%s' found!\n", bootInfo->bootFile);
1274gOverrideKernel = true;
1275}
1276}
1277
1278cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1279argP = bootArgs->CommandLine;
1280
1281// Get config kernel flags, if not ignored.
1282if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) ||
1283 !getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig ))
1284{
1285val = "";
1286cnt = 0;
1287}
1288configKernelFlags = malloc(cnt + 1);
1289strlcpy(configKernelFlags, val, cnt + 1);
1290
1291// boot-uuid can be set either on the command-line or in the config file
1292if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
1293 &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString)))
1294{
1295//
1296// Try an alternate method for getting the root UUID on boot helper partitions.
1297//
1298if (gBootVolume->flags & kBVFlagBooter)
1299{
1300// Load the configuration store in the boot helper partition
1301if (loadHelperConfig(&bootInfo->helperConfig) == 0)
1302{
1303val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
1304if (val != NULL)
1305{
1306strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
1307}
1308}
1309}
1310/*
1311// Try to get the volume uuid string
1312if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1313{
1314gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1315}
1316*/
1317// If we have the volume uuid add it to the commandline arguments
1318if (strlen(gBootUUIDString))
1319{
1320copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
1321}
1322// Try to get the volume uuid string
1323if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1324{
1325gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1326DBG("boot-uuid: %s\n", gBootUUIDString);
1327}
1328}
1329
1330if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
1331 &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE))
1332{
1333cnt = 0;
1334if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig))
1335{
1336valueBuffer[0] = '*';
1337cnt++;
1338strlcpy(valueBuffer + 1, val, cnt);
1339val = valueBuffer;
1340}
1341else
1342{ /*
1343if (strlen(gBootUUIDString))
1344{
1345val = "*uuid";
1346cnt = 5;
1347}
1348else
1349{ */
1350// Don't set "rd=.." if there is no boot device key
1351// and no UUID.
1352val = "";
1353cnt = 0;
1354/* } */
1355}
1356
1357if (cnt > 0)
1358{
1359copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1360}
1361strlcpy( gRootDevice, val, (cnt + 1));
1362}
1363
1364/*
1365 * Removed. We don't need this anymore.
1366 *
1367if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
1368 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler)))
1369{
1370getPlatformName(gPlatformName);
1371copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1372}
1373*/
1374
1375if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1376 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt))
1377{
1378if (gBootMode & kBootModeSafe)
1379{
1380copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1381}
1382}
1383
1384// Store the merged kernel flags and boot args.
1385
1386cnt = strlen(configKernelFlags);
1387if (cnt)
1388{
1389if (cnt > cntRemaining)
1390{
1391error("Warning: boot arguments too long, truncating\n");
1392cnt = cntRemaining;
1393}
1394strncpy(argP, configKernelFlags, cnt);
1395argP[cnt++] = ' ';
1396cntRemaining -= cnt;
1397}
1398userCnt = strlen(cp);
1399if (userCnt > cntRemaining)
1400{
1401error("Warning: boot arguments too long, truncating\n");
1402userCnt = cntRemaining;
1403}
1404strncpy(&argP[cnt], cp, userCnt);
1405argP[cnt+userCnt] = '\0';
1406
1407if(!shouldboot)
1408{
1409gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
1410getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
1411
1412gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
1413kBootModeSafe : kBootModeNormal;
1414
1415if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) )
1416{
1417gBootMode = kBootModeSafe;
1418}
1419}
1420
1421if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1422{
1423strlcpy(gMKextName, val, cnt + 1);
1424}
1425else
1426{
1427gMKextName[0]=0;
1428}
1429
1430free(configKernelFlags);
1431free(valueBuffer);
1432
1433return 0;
1434}
1435
1436
1437//==========================================================================
1438// Load the help file and display the file contents on the screen.
1439
1440void showTextBuffer(char *buf_orig, int size)
1441{
1442char*bp;
1443char* buf;
1444intline;
1445intline_offset;
1446intc;
1447
1448if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1449showInfoBox( "Press q to continue, space for next page.\n",buf_orig );
1450return;
1451}
1452
1453// Create a copy so that we don't mangle the original
1454buf = malloc(size + 1);
1455memcpy(buf, buf_orig, size);
1456
1457
1458 bp = buf;
1459 while (size-- > 0) {
1460if (*bp == '\n') {
1461*bp = '\0';
1462}
1463bp++;
1464 }
1465 *bp = '\1';
1466 line_offset = 0;
1467
1468 setActiveDisplayPage(1);
1469
1470 while (1) {
1471clearScreenRows(0, 24);
1472setCursorPosition(0, 0, 1);
1473bp = buf;
1474for (line = 0; *bp != '\1' && line < line_offset; line++) {
1475while (*bp != '\0') {
1476bp++;
1477}
1478bp++;
1479}
1480for (line = 0; *bp != '\1' && line < 23; line++) {
1481setCursorPosition(0, line, 1);
1482printf("%s\n", bp);
1483while (*bp != '\0') {
1484bp++;
1485}
1486bp++;
1487}
1488
1489setCursorPosition(0, 23, 1);
1490if (*bp == '\1') {
1491printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1492} else {
1493printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1494}
1495
1496c = getchar();
1497if (c == 'q' || c == 'Q') {
1498break;
1499}
1500if ((c == 'p' || c == 'P') && line_offset > 0) {
1501line_offset -= 23;
1502}
1503if (c == ' ') {
1504if (*bp == '\1') {
1505break;
1506} else {
1507line_offset += 23;
1508}
1509}
1510 }
1511 setActiveDisplayPage(0);
1512}
1513
1514void showHelp(void)
1515{
1516if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1517{
1518showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1519}
1520else
1521{
1522showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1523}
1524}
1525
1526void showTextFile(const char * filename)
1527{
1528#define MAX_TEXT_FILE_SIZE 65536
1529char*buf;
1530intfd;
1531intsize;
1532
1533if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0)
1534{
1535printf("\nFile not found: %s\n", filename);
1536sleep(2);
1537return;
1538}
1539
1540 size = file_size(fd);
1541 if (size > MAX_TEXT_FILE_SIZE)
1542{
1543size = MAX_TEXT_FILE_SIZE;
1544}
1545 buf = malloc(size);
1546 read(fd, buf, size);
1547 close(fd);
1548showTextBuffer(buf, size);
1549free(buf);
1550}
1551
1552// This is a very simplistic prompting scheme that just grabs two hex characters
1553// Eventually we need to do something more user-friendly like display a menu
1554// based off of the Multiboot device list
1555
1556int selectAlternateBootDevice(int bootdevice)
1557{
1558int key;
1559int newbootdevice;
1560int digitsI = 0;
1561char *end;
1562char digits[3] = {0,0,0};
1563
1564// We've already printed the current boot device so user knows what it is
1565printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1566printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1567do {
1568key = getchar();
1569switch (ASCII_KEY(key))
1570{
1571case KEY_BKSP:
1572if (digitsI > 0)
1573{
1574int x, y, t;
1575getCursorPositionAndType(&x, &y, &t);
1576// Assume x is not 0;
1577x--;
1578setCursorPosition(x,y,0); // back up one char
1579// Overwrite with space without moving cursor position
1580putca(' ', 0x07, 1);
1581digitsI--;
1582}
1583else
1584{
1585// TODO: Beep or something
1586}
1587break;
1588
1589case KEY_ENTER:
1590digits[digitsI] = '\0';
1591newbootdevice = strtol(digits, &end, 16);
1592if (end == digits && *end == '\0')
1593{
1594// User entered empty string
1595printf("\nUsing default boot device %x\n", bootdevice);
1596key = 0;
1597} else if(end != digits && *end == '\0') {
1598bootdevice = newbootdevice;
1599printf("\n");
1600key = 0; // We gots da boot device
1601} else {
1602printf("\nCouldn't parse. try again: ");
1603digitsI = 0;
1604}
1605break;
1606
1607default:
1608if (isxdigit(ASCII_KEY(key)) && digitsI < 2)
1609{
1610putchar(ASCII_KEY(key));
1611digits[digitsI++] = ASCII_KEY(key);
1612}
1613else
1614{
1615// TODO: Beep or something
1616}
1617break;
1618};
1619} while (key != 0);
1620
1621return bootdevice;
1622}
1623
1624bool promptForRescanOption(void)
1625{
1626printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1627if (getchar() == KEY_ENTER)
1628{
1629return true;
1630}
1631else
1632{
1633return false;
1634}
1635}
1636

Archive Download this file

Revision: 2483