/************************************************************************/ /* KEGS: Apple //gs Emulator */ /* Copyright 2003 by Kent Dickey */ /* */ /* This code is covered by the GNU GPL */ /* */ /* The KEGS web page is kegs.sourceforge.net */ /* You may contact the author at: kadickey@alumni.princeton.edu */ /************************************************************************/ const char rcsid_config_c[] = "@(#)$KmKId: config.c,v 1.50 2004-12-04 02:05:25-05 kentd Exp $"; #include "defc.h" #include #include "config.h" #include extern int Verbose; extern word32 g_vbl_count; extern Iwm iwm; extern int g_track_bytes_35[]; extern int g_track_nibs_35[]; extern int g_c031_disk35; extern int g_cur_a2_stat; extern byte *g_slow_memory_ptr; extern byte *g_rom_fc_ff_ptr; extern byte *g_rom_cards_ptr; extern double g_cur_dcycs; extern int g_rom_version; extern int g_fatal_log; extern word32 g_adb_repeat_vbl; extern int g_limit_speed; extern int g_force_depth; extern int g_raw_serial; extern int g_serial_out_masking; extern int g_serial_modem[]; extern word32 g_mem_size_base; extern word32 g_mem_size_exp; extern int g_video_line_update_interval; extern int g_video_extra_check_inputs; extern int g_user_halt_bad; extern int g_joystick_type; extern int g_joystick_scale_factor_x; extern int g_joystick_scale_factor_y; extern int g_joystick_trim_amount_x; extern int g_joystick_trim_amount_y; extern int g_swap_paddles; extern int g_invert_paddles; extern int g_screen_index[]; extern word32 g_full_refresh_needed; extern word32 g_a2_screen_buffer_changed; extern int g_a2_new_all_stat[]; extern int g_new_a2_stat_cur_line; extern int g_key_down; extern const char g_kegs_version_str[]; int g_config_control_panel = 0; char g_config_kegs_name[1024]; char g_cfg_cwd_str[CFG_PATH_MAX] = { 0 }; int g_config_kegs_auto_update = 1; int g_config_kegs_update_needed = 0; const char *g_config_kegs_name_list[] = { "config.kegs", "kegs_conf", ".config.kegs", 0 }; int g_highest_smartport_unit = -1; int g_reparse_delay = 0; int g_user_page2_shadow = 1; byte g_save_text_screen_bytes[0x800]; int g_save_cur_a2_stat = 0; char g_cfg_printf_buf[CFG_PRINTF_BUFSIZE]; char g_config_kegs_buf[CONF_BUF_LEN]; word32 g_cfg_vbl_count = 0; int g_cfg_curs_x = 0; int g_cfg_curs_y = 0; int g_cfg_curs_inv = 0; int g_cfg_curs_mousetext = 0; #define CFG_MAX_OPTS 16 #define CFG_OPT_MAXSTR 100 int g_cfg_opts_vals[CFG_MAX_OPTS]; char g_cfg_opts_strs[CFG_MAX_OPTS][CFG_OPT_MAXSTR]; char g_cfg_opt_buf[CFG_OPT_MAXSTR]; char *g_cfg_rom_path = "ROM"; char *g_cfg_file_def_name = "Undefined"; char **g_cfg_file_strptr = 0; int g_cfg_file_min_size = 1024; int g_cfg_file_max_size = 2047*1024*1024; #define MAX_PARTITION_BLK_SIZE 65536 extern Cfg_menu g_cfg_main_menu[]; #define KNMP(a) &a, #a, 0 Cfg_menu g_cfg_disk_menu[] = { { "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, { "s5d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x5000 }, { "s5d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x5010 }, { "", 0, 0, 0, 0 }, { "s6d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x6000 }, { "s6d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x6010 }, { "", 0, 0, 0, 0 }, { "s7d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x7000 }, { "s7d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x7010 }, { "s7d3 = ", 0, 0, 0, CFGTYPE_DISK + 0x7020 }, { "s7d4 = ", 0, 0, 0, CFGTYPE_DISK + 0x7030 }, { "s7d5 = ", 0, 0, 0, CFGTYPE_DISK + 0x7040 }, { "s7d6 = ", 0, 0, 0, CFGTYPE_DISK + 0x7050 }, { "s7d7 = ", 0, 0, 0, CFGTYPE_DISK + 0x7060 }, { "s7d8 = ", 0, 0, 0, CFGTYPE_DISK + 0x7070 }, { "s7d9 = ", 0, 0, 0, CFGTYPE_DISK + 0x7080 }, { "s7d10 = ", 0, 0, 0, CFGTYPE_DISK + 0x7090 }, { "s7d11 = ", 0, 0, 0, CFGTYPE_DISK + 0x70a0 }, { "", 0, 0, 0, 0 }, { "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { 0, 0, 0, 0, 0 }, }; Cfg_menu g_cfg_joystick_menu[] = { { "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, { "Joystick Emulation,0,Keypad Joystick,1,Mouse Joystick,2,Native Joystick 1," "3,Native Joystick 2", KNMP(g_joystick_type), CFGTYPE_INT }, { "Joystick Scale X,0x100,Standard,0x119,+10%,0x133,+20%," "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", KNMP(g_joystick_scale_factor_x), CFGTYPE_INT }, { "Joystick Scale Y,0x100,Standard,0x119,+10%,0x133,+20%," "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", KNMP(g_joystick_scale_factor_y), CFGTYPE_INT }, { "Joystick Trim X", KNMP(g_joystick_trim_amount_x), CFGTYPE_INT }, { "Joystick Trim Y", KNMP(g_joystick_trim_amount_y), CFGTYPE_INT }, { "Swap Joystick X and Y,0,Normal operation,1,Paddle 1 and Paddle 0 swapped", KNMP(g_swap_paddles), CFGTYPE_INT }, { "Invert Joystick,0,Normal operation,1,Left becomes right and up becomes down", KNMP(g_invert_paddles), CFGTYPE_INT }, { "", 0, 0, 0, 0 }, { "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { 0, 0, 0, 0, 0 }, }; Cfg_menu g_cfg_rom_menu[] = { { "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, { "ROM File", KNMP(g_cfg_rom_path), CFGTYPE_FILE }, { "", 0, 0, 0, 0 }, { "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { 0, 0, 0, 0, 0 }, }; Cfg_menu g_cfg_serial_menu[] = { { "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, { "Serial Ports,0,Only use sockets 6501-6502,1,Use real ports if avail", KNMP(g_raw_serial), CFGTYPE_INT }, { "Serial Output,0,Send full 8-bit data,1,Mask off high bit", KNMP(g_serial_out_masking), CFGTYPE_INT }, { "Modem on port 0 (slot 1),0,Simple socket emulation mode,1,Modem with " "incoming and outgoing emulation", KNMP(g_serial_modem[0]), CFGTYPE_INT }, { "Modem on port 1 (slot 2),0,Simple socket emulation mode,1,Modem with " "incoming and outgoing emulation", KNMP(g_serial_modem[1]), CFGTYPE_INT }, { "", 0, 0, 0, 0 }, { "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { 0, 0, 0, 0, 0 }, }; Cfg_menu g_cfg_main_menu[] = { { "KEGS Configuration", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, { "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, { "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, { "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, { "Force X-windows display depth", KNMP(g_force_depth), CFGTYPE_INT }, { "Auto-update config.kegs,0,Manual,1,Immediately", KNMP(g_config_kegs_auto_update), CFGTYPE_INT }, { "Speed,0,Unlimited,1,1.0MHz,2,2.8MHz,3,8.0MHz (Zip)", KNMP(g_limit_speed), CFGTYPE_INT }, { "Expansion Mem Size,0,0MB,0x100000,1MB,0x200000,2MB,0x300000,3MB," "0x400000,4MB,0x600000,6MB,0x800000,8MB,0xa00000,10MB,0xc00000,12MB," "0xe00000,14MB", KNMP(g_mem_size_exp), CFGTYPE_INT }, { "3200 Color Enable,0,Auto (Full if fast enough),1,Full (Update every line)," "8,Off (Update video every 8 lines)", KNMP(g_video_line_update_interval), CFGTYPE_INT }, { "Keyboard and mouse poll rate,0,60 times per second,1,240 times per second", KNMP(g_video_extra_check_inputs), CFGTYPE_INT }, { "Code Red Halts,0,Do not stop on bad accesses,1,Enter debugger on bad " "accesses", KNMP(g_user_halt_bad), CFGTYPE_INT }, { "Enable Text Page 2 Shadow,0,Disabled on ROM 01 (matches real hardware)," "1,Enabled on ROM 01 and 03", KNMP(g_user_page2_shadow), CFGTYPE_INT }, { "Dump text screen to file", (void *)cfg_text_screen_dump, 0, 0, CFGTYPE_FUNC}, { "", 0, 0, 0, 0 }, { "Save changes to config.kegs", (void *)config_write_config_kegs_file, 0, 0, CFGTYPE_FUNC }, { "", 0, 0, 0, 0 }, { "Exit Config (or press F4)", (void *)cfg_exit, 0, 0, CFGTYPE_FUNC }, { 0, 0, 0, 0, 0 }, }; #define CFG_MAX_DEFVALS 128 Cfg_defval g_cfg_defvals[CFG_MAX_DEFVALS]; int g_cfg_defval_index = 0; int g_cfg_slotdrive = -1; int g_cfg_select_partition = -1; char g_cfg_tmp_path[CFG_PATH_MAX]; char g_cfg_file_path[CFG_PATH_MAX]; char g_cfg_file_cachedpath[CFG_PATH_MAX]; char g_cfg_file_cachedreal[CFG_PATH_MAX]; char g_cfg_file_curpath[CFG_PATH_MAX]; char g_cfg_file_shortened[CFG_PATH_MAX]; char g_cfg_file_match[CFG_PATH_MAX]; Cfg_listhdr g_cfg_dirlist = { 0 }; Cfg_listhdr g_cfg_partitionlist = { 0 }; int g_cfg_file_pathfield = 0; const char *g_kegs_rom_names[] = { "ROM", "ROM", "ROM.01", "ROM.03", 0 }; /* First entry is special--it will be overwritten by g_cfg_rom_path */ const char *g_kegs_c1rom_names[] = { 0 }; const char *g_kegs_c2rom_names[] = { 0 }; const char *g_kegs_c3rom_names[] = { 0 }; const char *g_kegs_c4rom_names[] = { 0 }; const char *g_kegs_c5rom_names[] = { 0 }; const char *g_kegs_c6rom_names[] = { "c600.rom", "controller.rom", "disk.rom", "DISK.ROM", "diskII.prom", 0 }; const char *g_kegs_c7rom_names[] = { 0 }; const char **g_kegs_rom_card_list[8] = { 0, g_kegs_c1rom_names, g_kegs_c2rom_names, g_kegs_c3rom_names, g_kegs_c4rom_names, g_kegs_c5rom_names, g_kegs_c6rom_names, g_kegs_c7rom_names }; byte g_rom_c600_rom01_diffs[256] = { 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xe2, 0x00, 0xd0, 0x50, 0x0f, 0x77, 0x5b, 0xb9, 0xc3, 0xb1, 0xb1, 0xf8, 0xcb, 0x4e, 0xb8, 0x60, 0xc7, 0x2e, 0xfc, 0xe0, 0xbf, 0x1f, 0x66, 0x37, 0x4a, 0x70, 0x55, 0x2c, 0x3c, 0xfc, 0xc2, 0xa5, 0x08, 0x29, 0xac, 0x21, 0xcc, 0x09, 0x55, 0x03, 0x17, 0x35, 0x4e, 0xe2, 0x0c, 0xe9, 0x3f, 0x9d, 0xc2, 0x06, 0x18, 0x88, 0x0d, 0x58, 0x57, 0x6d, 0x83, 0x8c, 0x22, 0xd3, 0x4f, 0x0a, 0xe5, 0xb7, 0x9f, 0x7d, 0x2c, 0x3e, 0xae, 0x7f, 0x24, 0x78, 0xfd, 0xd0, 0xb5, 0xd6, 0xe5, 0x26, 0x85, 0x3d, 0x8d, 0xc9, 0x79, 0x0c, 0x75, 0xec, 0x98, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x6c, 0x44, 0xce, 0x4c, 0x01, 0x08, 0x00, 0x00 }; void config_init_menus(Cfg_menu *menuptr) { void *voidptr; const char *name_str; Cfg_defval *defptr; char **str_ptr; char *str; int type; int pos; int val; if(menuptr[0].defptr != 0) { return; } menuptr[0].defptr = (void *)1; pos = 0; while(pos < 100) { type = menuptr->cfgtype; voidptr = menuptr->ptr; name_str = menuptr->name_str; if(menuptr->str == 0) { break; } if(name_str != 0) { defptr = &(g_cfg_defvals[g_cfg_defval_index++]); if(g_cfg_defval_index >= CFG_MAX_DEFVALS) { fatal_printf("CFG_MAX_DEFVAL overflow\n"); my_exit(5); } defptr->menuptr = menuptr; defptr->intval = 0; defptr->strval = 0; switch(type) { case CFGTYPE_INT: val = *((int *)voidptr); defptr->intval = val; menuptr->defptr = &(defptr->intval); break; case CFGTYPE_FILE: str_ptr = (char **)menuptr->ptr; str = *str_ptr; // We need to malloc this string since all // string values must be dynamically alloced defptr->strval = str; // this can have a copy *str_ptr = kegs_malloc_str(str); menuptr->defptr = &(defptr->strval); break; default: fatal_printf("name_str is %p = %s, but type: " "%d\n", name_str, name_str, type); my_exit(5); } } if(type == CFGTYPE_MENU) { config_init_menus((Cfg_menu *)voidptr); } pos++; menuptr++; } } void config_init() { int can_create; config_init_menus(g_cfg_main_menu); // Find the config.kegs file g_config_kegs_name[0] = 0; can_create = 1; setup_kegs_file(&g_config_kegs_name[0], sizeof(g_config_kegs_name), 0, can_create, &g_config_kegs_name_list[0]); config_parse_config_kegs_file(); } void cfg_exit() { /* printf("In cfg exit\n"); */ if(g_rom_version >= 1) { g_config_control_panel = 0; } } void cfg_toggle_config_panel() { g_config_control_panel = !g_config_control_panel; if(g_rom_version < 0) { g_config_control_panel = 1; /* Stay in config mode */ } } void cfg_text_screen_dump() { char buf[85]; char *filename; FILE *ofile; int offset; int c; int pos; int i, j; filename = "kegs.screen.dump"; printf("Writing text screen to the file %s\n", filename); ofile = fopen(filename, "w"); if(ofile == 0) { fatal_printf("Could not write to file %s, (%d)\n", filename, errno); return; } for(i = 0; i < 24; i++) { pos = 0; for(j = 0; j < 40; j++) { offset = g_screen_index[i] + j; if(g_save_cur_a2_stat & ALL_STAT_VID80) { c = g_save_text_screen_bytes[0x400+offset]; c = c & 0x7f; if(c < 0x20) { c += 0x40; } buf[pos++] = c; } c = g_save_text_screen_bytes[offset] & 0x7f; if(c < 0x20) { c += 0x40; } buf[pos++] = c; } while((pos > 0) && (buf[pos-1] == ' ')) { /* try to strip out trailing spaces */ pos--; } buf[pos++] = '\n'; buf[pos++] = 0; fputs(buf, ofile); } fclose(ofile); } void config_vbl_update(int doit_3_persec) { if(doit_3_persec) { if(g_config_kegs_auto_update && g_config_kegs_update_needed) { config_write_config_kegs_file(); } } return; } void config_parse_option(char *buf, int pos, int len, int line) { Cfg_menu *menuptr; Cfg_defval *defptr; char *nameptr; char **strptr; int *iptr; int num_equals; int type; int val; int c; int i; // warning: modifies buf (turns spaces to nulls) // parse buf from pos into option, "=" and then "rest" if(pos >= len) { /* blank line */ return; } if(strncmp(&buf[pos], "bram", 4) == 0) { config_parse_bram(buf, pos+4, len); return; } // find "name" as first contiguous string printf("...parse_option: line %d, %p,%p = %s (%s) len:%d\n", line, &buf[pos], buf, &buf[pos], buf, len); nameptr = &buf[pos]; while(pos < len) { c = buf[pos]; if(c == 0 || c == ' ' || c == '\t' || c == '\n') { break; } pos++; } buf[pos] = 0; pos++; // Eat up all whitespace and '=' num_equals = 0; while(pos < len) { c = buf[pos]; if((c == '=') && num_equals == 0) { pos++; num_equals++; } else if(c == ' ' || c == '\t') { pos++; } else { break; } } /* Look up nameptr to find type */ type = -1; defptr = 0; menuptr = 0; for(i = 0; i < g_cfg_defval_index; i++) { defptr = &(g_cfg_defvals[i]); menuptr = defptr->menuptr; if(strcmp(menuptr->name_str, nameptr) == 0) { type = menuptr->cfgtype; break; } } switch(type) { case CFGTYPE_INT: /* use strtol */ val = (int)strtol(&buf[pos], 0, 0); iptr = (int *)menuptr->ptr; *iptr = val; break; case CFGTYPE_FILE: strptr = (char **)menuptr->ptr; if(strptr && *strptr) { free(*strptr); } *strptr = kegs_malloc_str(&buf[pos]); break; default: printf("Config file variable %s is unknown type: %d\n", nameptr, type); } } void config_parse_bram(char *buf, int pos, int len) { int bram_num; int offset; int val; if((len < (pos+5)) || (buf[pos+1] != '[') || (buf[pos+4] != ']')) { fatal_printf("While reading config.kegs, found malformed bram " "statement: %s\n", buf); return; } bram_num = buf[pos] - '0'; if(bram_num != 1 && bram_num != 3) { fatal_printf("While reading config.kegs, found bad bram " "num: %s\n", buf); return; } bram_num = bram_num >> 1; // turn 3->1 and 1->0 offset = strtoul(&(buf[pos+2]), 0, 16); pos += 5; while(pos < len) { while(buf[pos] == ' ' || buf[pos] == '\t' || buf[pos] == 0x0a || buf[pos] == 0x0d || buf[pos] == '=') { pos++; } val = strtoul(&buf[pos], 0, 16); clk_bram_set(bram_num, offset, val); offset++; pos += 2; } } void config_load_roms() { struct stat stat_buf; const char **names_ptr; int more_than_8mb; int changed_rom; int len; int fd; int ret; int i; g_rom_version = -1; /* set first entry of g_kegs_rom_names[] to g_cfg_rom_path so that */ /* it becomes the first place searched. */ g_kegs_rom_names[0] = g_cfg_rom_path; setup_kegs_file(&g_cfg_tmp_path[0], CFG_PATH_MAX, -1, 0, &g_kegs_rom_names[0]); if(g_cfg_tmp_path[0] == 0) { // Just get out, let config interface select ROM g_config_control_panel = 1; return; } fd = open(&g_cfg_tmp_path[0], O_RDONLY | O_BINARY); if(fd < 0) { fatal_printf("Open ROM file %s failed:%d, errno:%d\n", &g_cfg_tmp_path[0], fd, errno); g_config_control_panel = 1; return; } ret = fstat(fd, &stat_buf); if(ret != 0) { fatal_printf("fstat returned %d on fd %d, errno: %d\n", ret, fd, errno); g_config_control_panel = 1; return; } len = stat_buf.st_size; if(len == 128*1024) { g_rom_version = 1; g_mem_size_base = 256*1024; memset(&g_rom_fc_ff_ptr[0], 0, 2*65536); /* Clear banks fc and fd to 0 */ ret = read(fd, &g_rom_fc_ff_ptr[2*65536], len); } else if(len == 256*1024) { g_rom_version = 3; g_mem_size_base = 1024*1024; ret = read(fd, &g_rom_fc_ff_ptr[0], len); } else { fatal_printf("The ROM size should be 128K or 256K, this file " "is %d bytes\n", len); g_config_control_panel = 1; return; } printf("Read: %d bytes of ROM\n", ret); if(ret != len) { fatal_printf("errno: %d\n", errno); g_config_control_panel = 1; return; } close(fd); memset(&g_rom_cards_ptr[0], 0, 256*16); /* initialize c600 rom to be diffs from the real ROM, to build-in */ /* Apple II compatibility without distributing ROMs */ for(i = 0; i < 256; i++) { g_rom_cards_ptr[0x600 + i] = g_rom_fc_ff_ptr[0x3c600 + i] ^ g_rom_c600_rom01_diffs[i]; } if(g_rom_version >= 3) { /* some patches */ g_rom_cards_ptr[0x61b] ^= 0x40; g_rom_cards_ptr[0x61c] ^= 0x33; g_rom_cards_ptr[0x632] ^= 0xc0; g_rom_cards_ptr[0x633] ^= 0x33; } for(i = 1; i < 8; i++) { names_ptr = g_kegs_rom_card_list[i]; if(names_ptr == 0) { continue; } if(*names_ptr == 0) { continue; } setup_kegs_file(&g_cfg_tmp_path[0], CFG_PATH_MAX, 1, 0, names_ptr); if(g_cfg_tmp_path[0] != 0) { fd = open(&(g_cfg_tmp_path[0]), O_RDONLY | O_BINARY); if(fd < 0) { fatal_printf("Open card ROM file %s failed: %d " "err:%d\n", &g_cfg_tmp_path[0], fd, errno); continue; } len = 256; ret = read(fd, &g_rom_cards_ptr[i*0x100], len); if(ret != len) { fatal_printf("While reading card ROM %s, file " "is too short. (%d) Expected %d bytes, " "read %d bytes\n", errno, len, ret); continue; } close(fd); } } more_than_8mb = (g_mem_size_exp > 0x800000); /* Only do the patch if users wants more than 8MB of expansion mem */ changed_rom = 0; if(g_rom_version == 1) { /* make some patches to ROM 01 */ #if 0 /* 1: Patch ROM selftest to not do speed test */ printf("Patching out speed test failures from ROM 01\n"); g_rom_fc_ff_ptr[0x3785a] = 0x18; changed_rom = 1; #endif #if 0 /* 2: Patch ROM selftests not to do tests 2,4 */ /* 0 = skip, 1 = do it, test 1 is bit 0 of LSByte */ g_rom_fc_ff_ptr[0x371e9] = 0xf5; g_rom_fc_ff_ptr[0x371ea] = 0xff; changed_rom = 1; #endif if(more_than_8mb) { /* Geoff Weiss patch to use up to 14MB of RAM */ g_rom_fc_ff_ptr[0x30302] = 0xdf; g_rom_fc_ff_ptr[0x30314] = 0xdf; g_rom_fc_ff_ptr[0x3031c] = 0x00; changed_rom = 1; } /* Patch ROM selftest to not do ROM cksum if any changes*/ if(changed_rom) { g_rom_fc_ff_ptr[0x37a06] = 0x18; g_rom_fc_ff_ptr[0x37a07] = 0x18; } } else if(g_rom_version == 3) { /* patch ROM 03 */ printf("Patching ROM 03 smartport bug\n"); /* 1: Patch Smartport code to fix a stupid bug */ /* that causes it to write the IWM status reg into c036, */ /* which is the system speed reg...it's "safe" since */ /* IWM status reg bit 4 must be 0 (7MHz)..., otherwise */ /* it might have turned on shadowing in all banks! */ g_rom_fc_ff_ptr[0x357c9] = 0x00; changed_rom = 1; #if 0 /* patch ROM 03 to not to speed test */ /* skip fast speed test */ g_rom_fc_ff_ptr[0x36ad7] = 0x18; g_rom_fc_ff_ptr[0x36ad8] = 0x18; changed_rom = 1; #endif #if 0 /* skip slow speed test */ g_rom_fc_ff_ptr[0x36ae7] = 0x18; g_rom_fc_ff_ptr[0x36ae8] = 0x6b; changed_rom = 1; #endif #if 0 /* 4: Patch ROM 03 selftests not to do tests 1-4 */ g_rom_fc_ff_ptr[0x364a9] = 0xf0; g_rom_fc_ff_ptr[0x364aa] = 0xff; changed_rom = 1; #endif /* ROM tests are in ff/6403-642x, where 6403 = addr of */ /* test 1, etc. */ if(more_than_8mb) { /* Geoff Weiss patch to use up to 14MB of RAM */ g_rom_fc_ff_ptr[0x30b] = 0xdf; g_rom_fc_ff_ptr[0x31d] = 0xdf; g_rom_fc_ff_ptr[0x325] = 0x00; changed_rom = 1; } if(changed_rom) { /* patch ROM 03 selftest to not do ROM cksum */ g_rom_fc_ff_ptr[0x36cb0] = 0x18; g_rom_fc_ff_ptr[0x36cb1] = 0x18; } } } void config_parse_config_kegs_file() { FILE *fconf; char *buf; char *ptr; char *name_ptr; char *partition_name; int part_num; int ejected; int line; int pos; int slot; int drive; int size; int len; int ret; int i; printf("Parsing config.kegs file\n"); clk_bram_zero(); g_highest_smartport_unit = -1; cfg_get_base_path(&g_cfg_cwd_str[0], g_config_kegs_name, 0); if(g_cfg_cwd_str[0] != 0) { ret = chdir(&g_cfg_cwd_str[0]); if(ret != 0) { printf("chdir to %s, errno:%d\n", g_cfg_cwd_str, errno); } } /* In any case, copy the directory path to g_cfg_cwd_str */ (void)getcwd(&g_cfg_cwd_str[0], CFG_PATH_MAX); fconf = fopen(g_config_kegs_name, "r"); if(fconf == 0) { fatal_printf("cannot open config.kegs at %s! Stopping!\n", g_config_kegs_name); my_exit(3); } line = 0; while(1) { buf = &g_config_kegs_buf[0]; ptr = fgets(buf, CONF_BUF_LEN, fconf); if(ptr == 0) { iwm_printf("Done reading disk_conf\n"); break; } line++; /* strip off newline(s) */ len = strlen(buf); for(i = len - 1; i >= 0; i--) { if((buf[i] != 0x0d) && (buf[i] != 0x0a)) { break; } len = i; buf[i] = 0; } iwm_printf("disk_conf[%d]: %s\n", line, buf); if(len > 0 && buf[0] == '#') { iwm_printf("Skipping comment\n"); continue; } /* determine what this is */ pos = 0; while(pos < len && (buf[pos] == ' ' || buf[pos] == '\t') ) { pos++; } if((pos + 4) > len || buf[pos] != 's' || buf[pos+2] != 'd' || buf[pos+1] > '9' || buf[pos+1] < '0') { config_parse_option(buf, pos, len, line); continue; } slot = buf[pos+1] - '0'; drive = buf[pos+3] - '0'; /* skip over slot, drive */ pos += 4; if(buf[pos] >= '0' && buf[pos] <= '9') { drive = drive * 10 + buf[pos] - '0'; pos++; } /* make s6d1 mean index 0 */ drive--; while(pos < len && (buf[pos] == ' ' || buf[pos] == '\t' || buf[pos] == '=') ) { pos++; } ejected = 0; if(buf[pos] == '#') { /* disk is ejected, but read all the info anyway */ ejected = 1; pos++; } size = 0; if(buf[pos] == ',') { /* read optional size parameter */ pos++; while(pos < len && buf[pos] >= '0' && buf[pos] <= '9'){ size = size * 10 + buf[pos] - '0'; pos++; } size = size * 1024; if(buf[pos] == ',') { pos++; /* eat trailing ',' */ } } /* see if it has a partition name */ partition_name = 0; part_num = -1; if(buf[pos] == ':') { pos++; /* yup, it's got a partition name! */ partition_name = &buf[pos]; while((pos < len) && (buf[pos] != ':')) { pos++; } buf[pos] = 0; /* null terminate partition name */ pos++; } if(buf[pos] == ';') { pos++; /* it's got a partition number */ part_num = 0; while((pos < len) && (buf[pos] != ':')) { part_num = (10*part_num) + buf[pos] - '0'; pos++; } pos++; } /* Get filename */ name_ptr = &(buf[pos]); if(name_ptr[0] == 0) { continue; } insert_disk(slot, drive, name_ptr, ejected, size, partition_name, part_num); } ret = fclose(fconf); if(ret != 0) { fatal_printf("Closing config.kegs ret: %d, errno: %d\n", ret, errno); my_exit(4); } iwm_printf("Done parsing disk_conf file\n"); } Disk * cfg_get_dsk_from_slot_drive(int slot, int drive) { Disk *dsk; int max_drive; /* Get dsk */ max_drive = 2; switch(slot) { case 5: dsk = &(iwm.drive35[drive]); break; case 6: dsk = &(iwm.drive525[drive]); break; default: max_drive = MAX_C7_DISKS; dsk = &(iwm.smartport[drive]); } if(drive >= max_drive) { dsk -= drive; /* move back to drive 0 effectively */ } return dsk; } void config_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk, int with_extras) { char *str; str = outstr; if(with_extras && dsk->fd < 0) { snprintf(str, maxlen - (str - outstr), "#"); str = &outstr[strlen(outstr)]; } if(with_extras && dsk->force_size > 0) { snprintf(str, maxlen - (str - outstr), ",%d,", dsk->force_size); str = &outstr[strlen(outstr)]; } if(with_extras && dsk->partition_name != 0) { snprintf(str, maxlen - (str - outstr), ":%s:", dsk->partition_name); str = &outstr[strlen(outstr)]; } else if(with_extras && dsk->partition_num >= 0) { snprintf(str, maxlen - (str - outstr), ";%d:", dsk->partition_num); str = &outstr[strlen(outstr)]; } snprintf(str, maxlen - (str - outstr), "%s", dsk->name_ptr); } void config_write_config_kegs_file() { FILE *fconf; Disk *dsk; Cfg_defval *defptr; Cfg_menu *menuptr; char *curstr, *defstr; int defval, curval; int type; int slot, drive; int i; printf("Writing config.kegs file to %s\n", g_config_kegs_name); fconf = fopen(g_config_kegs_name, "w+"); if(fconf == 0) { halt_printf("cannot open %s! Stopping!\n"); return; } fprintf(fconf, "# KEGS configuration file version %s\n", g_kegs_version_str); for(i = 0; i < MAX_C7_DISKS + 4; i++) { slot = 7; drive = i - 4; if(i < 4) { slot = (i >> 1) + 5; drive = i & 1; } if(drive == 0) { fprintf(fconf, "\n"); /* an extra blank line */ } dsk = cfg_get_dsk_from_slot_drive(slot, drive); if(dsk->name_ptr == 0 && (i > 4)) { /* No disk, not even ejected--just skip */ continue; } fprintf(fconf, "s%dd%d = ", slot, drive + 1); if(dsk->name_ptr == 0) { fprintf(fconf, "\n"); continue; } config_generate_config_kegs_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, dsk, 1); fprintf(fconf, "%s\n", &g_cfg_tmp_path[0]); } fprintf(fconf, "\n"); /* See if any variables are different than their default */ for(i = 0; i < g_cfg_defval_index; i++) { defptr = &(g_cfg_defvals[i]); menuptr = defptr->menuptr; defval = defptr->intval; type = menuptr->cfgtype; if(type == CFGTYPE_INT) { curval = *((int *)menuptr->ptr); if(curval != defval) { fprintf(fconf, "%s = %d\n", menuptr->name_str, curval); } } if(type == CFGTYPE_FILE) { curstr = *((char **)menuptr->ptr); defstr = *((char **)menuptr->defptr); if(strcmp(curstr, defstr) != 0) { fprintf(fconf, "%s = %s\n", menuptr->name_str, curstr); } } } fprintf(fconf, "\n"); /* write bram state */ clk_write_bram(fconf); fclose(fconf); g_config_kegs_update_needed = 0; } void insert_disk(int slot, int drive, const char *name, int ejected, int force_size, const char *partition_name, int part_num) { byte buf_2img[512]; Disk *dsk; char *name_ptr, *uncomp_ptr, *system_str; char *part_ptr; int size; int system_len; int part_len; int cmp_o, cmp_p, cmp_dot; int cmp_b, cmp_i, cmp_n; int can_write; int len; int nibs; int unix_pos; int name_len; int image_identified; int exp_size; int save_track; int ret; int tmp; int i; g_config_kegs_update_needed = 1; if((slot < 5) || (slot > 7)) { fatal_printf("Invalid slot for insertiing disk: %d\n", slot); return; } if(drive < 0 || ((slot == 7) && (drive >= MAX_C7_DISKS)) || ((slot < 7) && (drive > 1))) { fatal_printf("Invalid drive for inserting disk: %d\n", drive); return; } dsk = cfg_get_dsk_from_slot_drive(slot, drive); #if 0 printf("Inserting disk %s (%s or %d) in slot %d, drive: %d\n", name, partition_name, part_num, slot, drive); #endif dsk->just_ejected = 0; dsk->force_size = force_size; if(dsk->fd >= 0) { eject_disk(dsk); } /* Before opening, make sure no other mounted disk has this name */ /* If so, unmount it */ if(!ejected) { for(i = 0; i < 2; i++) { eject_named_disk(&iwm.drive525[i], name,partition_name); eject_named_disk(&iwm.drive35[i], name, partition_name); } for(i = 0; i < MAX_C7_DISKS; i++) { eject_named_disk(&iwm.smartport[i],name,partition_name); } } if(dsk->name_ptr != 0) { /* free old name_ptr */ free(dsk->name_ptr); } name_len = strlen(name); name_ptr = (char *)malloc(name_len + 1); strncpy(name_ptr, name, name_len + 1); dsk->name_ptr = name_ptr; dsk->partition_name = 0; if(partition_name != 0) { part_len = strlen(partition_name) + 1; part_ptr = (char *)malloc(part_len); strncpy(part_ptr, partition_name, part_len); dsk->partition_name = part_ptr; } dsk->partition_num = part_num; iwm_printf("Opening up disk image named: %s\n", name_ptr); if(ejected) { /* just get out of here */ dsk->fd = -1; return; } dsk->fd = -1; can_write = 1; if((name_len > 3) && (strcmp(&name_ptr[name_len - 3], ".gz") == 0)) { /* it's gzip'ed, try to gunzip it, then unlink the */ /* uncompressed file */ can_write = 0; uncomp_ptr = (char *)malloc(name_len + 1); strncpy(uncomp_ptr, name_ptr, name_len + 1); uncomp_ptr[name_len - 3] = 0; system_len = 2*name_len + 100; system_str = (char *)malloc(system_len + 1); snprintf(system_str, system_len, "set -o noclobber;gunzip -c %c%s%c > %c%s%c", 0x22, name_ptr, 0x22, 0x22, uncomp_ptr, 0x22); /* 0x22 are " to allow spaces in filenames */ printf("I am uncompressing %s into %s for mounting\n", name_ptr, uncomp_ptr); ret = system(system_str); if(ret == 0) { /* successfully ran */ dsk->fd = open(uncomp_ptr, O_RDONLY | O_BINARY, 0x1b6); iwm_printf("Opening .gz file %s is fd: %d\n", uncomp_ptr, dsk->fd); /* and, unlink the temporary file */ (void)unlink(uncomp_ptr); } free(system_str); free(uncomp_ptr); /* Reduce name_len by 3 so that subsequent compares for .po */ /* look at the correct chars */ name_len -= 3; } if(dsk->fd < 0 && can_write) { dsk->fd = open(name_ptr, O_RDWR | O_BINARY, 0x1b6); } if(dsk->fd < 0 && can_write) { printf("Trying to open %s read-only, errno: %d\n", name_ptr, errno); dsk->fd = open(name_ptr, O_RDONLY | O_BINARY, 0x1b6); can_write = 0; } iwm_printf("open returned: %d\n", dsk->fd); if(dsk->fd < 0) { fatal_printf("Disk image %s does not exist!\n", name_ptr); return; } if(can_write != 0) { dsk->write_prot = 0; dsk->write_through_to_unix = 1; } else { dsk->write_prot = 1; dsk->write_through_to_unix = 0; } save_track = dsk->cur_qtr_track; /* save arm position */ dsk->image_type = DSK_TYPE_PRODOS; dsk->image_start = 0; /* See if it is in 2IMG format */ ret = read(dsk->fd, (char *)&buf_2img[0], 512); size = force_size; if(size <= 0) { size = cfg_get_fd_size(dsk->fd); } /* Try to guess that there is a Mac Binary header of 128 bytes */ /* See if image size & 0xfff = 0x080 which indicates extra 128 bytes */ if((size & 0xfff) == 0x080) { printf("Assuming Mac Binary header on %s\n", dsk->name_ptr); dsk->image_start += 0x80; } image_identified = 0; if(buf_2img[0] == '2' && buf_2img[1] == 'I' && buf_2img[2] == 'M' && buf_2img[3] == 'G') { /* It's a 2IMG disk */ printf("Image named %s is in 2IMG format\n", dsk->name_ptr); image_identified = 1; if(buf_2img[12] == 0) { printf("2IMG is in DOS 3.3 sector order\n"); dsk->image_type = DSK_TYPE_DOS33; } if(buf_2img[19] & 0x80) { /* disk is locked */ printf("2IMG is write protected\n"); dsk->write_prot = 1; dsk->write_through_to_unix = 0; } if((buf_2img[17] & 1) && (dsk->image_type == DSK_TYPE_DOS33)) { dsk->vol_num = buf_2img[16]; printf("Setting DOS 3.3 vol num to %d\n", dsk->vol_num); } // Some 2IMG archives have the size byte reversed size = (buf_2img[31] << 24) + (buf_2img[30] << 16) + (buf_2img[29] << 8) + buf_2img[28]; unix_pos = (buf_2img[27] << 24) + (buf_2img[26] << 16) + (buf_2img[25] << 8) + buf_2img[24]; if(size == 0x800c00) { // Byte reversed 0x0c8000 size = 0x0c8000; } dsk->image_start = unix_pos; dsk->image_size = size; } exp_size = 800*1024; if(dsk->disk_525) { exp_size = 140*1024; } if(!image_identified) { /* See if it might be the Mac diskcopy format */ tmp = (buf_2img[0x40] << 24) + (buf_2img[0x41] << 16) + (buf_2img[0x42] << 8) + buf_2img[0x43]; if((size >= (exp_size + 0x54)) && (tmp == exp_size)) { /* It's diskcopy since data size field matches */ printf("Image named %s is in Mac diskcopy format\n", dsk->name_ptr); image_identified = 1; dsk->image_start += 0x54; dsk->image_size = exp_size; dsk->image_type = DSK_TYPE_PRODOS; /* ProDOS */ } } if(!image_identified) { /* Assume raw image */ dsk->image_size = size; dsk->image_type = DSK_TYPE_PRODOS; if(dsk->disk_525) { dsk->image_type = DSK_TYPE_DOS33; if(name_len >= 4) { cmp_o = dsk->name_ptr[name_len-1]; cmp_p = dsk->name_ptr[name_len-2]; cmp_dot = dsk->name_ptr[name_len-3]; if(cmp_dot == '.' && (cmp_p == 'p' || cmp_p == 'P') && (cmp_o == 'o' || cmp_o == 'O')) { dsk->image_type = DSK_TYPE_PRODOS; } cmp_b = dsk->name_ptr[name_len-1]; cmp_i = dsk->name_ptr[name_len-2]; cmp_n = dsk->name_ptr[name_len-3]; cmp_dot = dsk->name_ptr[name_len-4]; if(cmp_dot == '.' && (cmp_n == 'n' || cmp_n == 'N') && (cmp_i == 'i' || cmp_i == 'I') && (cmp_b == 'b' || cmp_b == 'B')) { dsk->image_type = DSK_TYPE_NIB; dsk->write_prot = 1; dsk->write_through_to_unix = 0; } } } } dsk->disk_dirty = 0; dsk->nib_pos = 0; dsk->trks = 0; if(dsk->smartport) { g_highest_smartport_unit = MAX(dsk->drive, g_highest_smartport_unit); if(partition_name != 0 || part_num >= 0) { ret = cfg_partition_find_by_name_or_num(dsk->fd, partition_name, part_num, dsk); printf("partition %s (num %d) mounted, wr_prot: %d\n", partition_name, part_num, dsk->write_prot); if(ret < 0) { close(dsk->fd); dsk->fd = -1; return; } } iwm_printf("adding smartport device[%d], size:%08x, " "img_sz:%08x\n", dsk->drive, dsk->trks[0].unix_len, dsk->image_size); } else if(dsk->disk_525) { unix_pos = dsk->image_start; size = dsk->image_size; disk_set_num_tracks(dsk, 4*35); len = 0x1000; nibs = NIB_LEN_525; if(dsk->image_type == DSK_TYPE_NIB) { len = dsk->image_size / 35;; nibs = len; } if(size != 35*len) { fatal_printf("Disk 5.25 error: size is %d, not 140K. " "Will try to mount anyway\n", size, 35*len); } for(i = 0; i < 35; i++) { iwm_move_to_track(dsk, 4*i); disk_unix_to_nib(dsk, 4*i, unix_pos, len, nibs); unix_pos += len; } } else { /* disk_35 */ unix_pos = dsk->image_start; size = dsk->image_size; if(size != 800*1024) { fatal_printf("Disk 3.5 error: size is %d, not 800K. " "Will try to mount anyway\n", size); } disk_set_num_tracks(dsk, 2*80); for(i = 0; i < 2*80; i++) { iwm_move_to_track(dsk, i); len = g_track_bytes_35[i >> 5]; nibs = g_track_nibs_35[i >> 5]; iwm_printf("Trk: %d.%d = unix: %08x, %04x, %04x\n", i>>1, i & 1, unix_pos, len, nibs); disk_unix_to_nib(dsk, i, unix_pos, len, nibs); unix_pos += len; iwm_printf(" trk_len:%05x\n", dsk->trks[i].track_len); } } iwm_move_to_track(dsk, save_track); } void eject_named_disk(Disk *dsk, const char *name, const char *partition_name) { if(dsk->fd < 0) { return; } /* If name matches, eject the disk! */ if(!strcmp(dsk->name_ptr, name)) { /* It matches, eject it */ if((partition_name != 0) && (dsk->partition_name != 0)) { /* If both have partitions, and they differ, then */ /* don't eject. Otherwise, eject */ if(strcmp(dsk->partition_name, partition_name) != 0) { /* Don't eject */ return; } } eject_disk(dsk); } } void eject_disk_by_num(int slot, int drive) { Disk *dsk; dsk = cfg_get_dsk_from_slot_drive(slot, drive); eject_disk(dsk); } void eject_disk(Disk *dsk) { int motor_on; int i; if(dsk->fd < 0) { return; } g_config_kegs_update_needed = 1; motor_on = iwm.motor_on; if(g_c031_disk35 & 0x40) { motor_on = iwm.motor_on35; } if(motor_on) { halt_printf("Try eject dsk:%s, but motor_on!\n", dsk->name_ptr); } iwm_flush_disk_to_unix(dsk); printf("Ejecting disk: %s\n", dsk->name_ptr); /* Free all memory, close file */ /* free the tracks first */ if(dsk->trks != 0) { for(i = 0; i < dsk->num_tracks; i++) { if(dsk->trks[i].nib_area) { free(dsk->trks[i].nib_area); } dsk->trks[i].nib_area = 0; dsk->trks[i].track_len = 0; } free(dsk->trks); } dsk->num_tracks = 0; dsk->trks = 0; /* close file, clean up dsk struct */ close(dsk->fd); dsk->image_start = 0; dsk->image_size = 0; dsk->nib_pos = 0; dsk->disk_dirty = 0; dsk->write_through_to_unix = 0; dsk->write_prot = 1; dsk->fd = -1; dsk->just_ejected = 1; /* Leave name_ptr valid */ } int cfg_get_fd_size(int fd) { struct stat stat_buf; int ret; ret = fstat(fd, &stat_buf); if(ret != 0) { fprintf(stderr,"fstat returned %d on fd %d, errno: %d\n", ret, fd, errno); stat_buf.st_size = 0; } return stat_buf.st_size; } int cfg_partition_read_block(int fd, void *buf, int blk, int blk_size) { int ret; ret = lseek(fd, blk * blk_size, SEEK_SET); if(ret != blk * blk_size) { printf("lseek: %08x, wanted: %08x, errno: %d\n", ret, blk * blk_size, errno); return 0; } ret = read(fd, (char *)buf, blk_size); if(ret != blk_size) { printf("ret: %08x, wanted %08x, errno: %d\n", ret, blk_size, errno); return 0; } return ret; } int cfg_partition_find_by_name_or_num(int fd, const char *partnamestr, int part_num, Disk *dsk) { Cfg_dirent *direntptr; int match; int num_parts; int i; num_parts = cfg_partition_make_list(fd); if(num_parts <= 0) { return -1; } for(i = 0; i < g_cfg_partitionlist.last; i++) { direntptr = &(g_cfg_partitionlist.direntptr[i]); match = 0; if((strncmp(partnamestr, direntptr->name, 32) == 0) && (part_num < 0)) { //printf("partition, match1, name:%s %s, part_num:%d\n", // partnamestr, direntptr->name, part_num); match = 1; } if((partnamestr == 0) && (direntptr->part_num == part_num)) { //printf("partition, match2, n:%s, part_num:%d == %d\n", // direntptr->name, direntptr->part_num, part_num); match = 1; } if(match) { dsk->image_start = direntptr->image_start; dsk->image_size = direntptr->size; //printf("match with image_start: %08x, image_size: " // "%08x\n", dsk->image_start, dsk->image_size); return i; } } return -1; } int cfg_partition_make_list(int fd) { Driver_desc *driver_desc_ptr; Part_map *part_map_ptr; word32 *blk_bufptr; word32 start; word32 len; word32 data_off; word32 data_len; word32 sig; int size; int image_start, image_size; int is_dir; int block_size; int map_blks; int cur_blk; block_size = 512; cfg_free_alldirents(&g_cfg_partitionlist); blk_bufptr = (word32 *)malloc(MAX_PARTITION_BLK_SIZE); cfg_partition_read_block(fd, blk_bufptr, 0, block_size); driver_desc_ptr = (Driver_desc *)blk_bufptr; sig = GET_BE_WORD16(driver_desc_ptr->sig); block_size = GET_BE_WORD16(driver_desc_ptr->blk_size); if(block_size == 0) { block_size = 512; } if(sig != 0x4552 || block_size < 0x200 || (block_size > MAX_PARTITION_BLK_SIZE)) { cfg_printf("Partition error: No driver descriptor map found\n"); free(blk_bufptr); return 0; } map_blks = 1; cur_blk = 0; size = cfg_get_fd_size(fd); cfg_file_add_dirent(&g_cfg_partitionlist, "None - Whole image", is_dir=0, size, 0, -1); while(cur_blk < map_blks) { cur_blk++; cfg_partition_read_block(fd, blk_bufptr, cur_blk, block_size); part_map_ptr = (Part_map *)blk_bufptr; sig = GET_BE_WORD16(part_map_ptr->sig); if(cur_blk <= 1) { map_blks = MIN(20, GET_BE_WORD32(part_map_ptr->map_blk_cnt)); } if(sig != 0x504d) { printf("Partition entry %d bad signature:%04x\n", cur_blk, sig); free(blk_bufptr); return g_cfg_partitionlist.last; } /* found it, check for consistency */ start = GET_BE_WORD32(part_map_ptr->phys_part_start); len = GET_BE_WORD32(part_map_ptr->part_blk_cnt); data_off = GET_BE_WORD32(part_map_ptr->data_start); data_len = GET_BE_WORD32(part_map_ptr->data_cnt); if(data_off + data_len > len) { printf("Poorly formed entry\n"); continue; } if(data_len < 10 || start < 1) { printf("Poorly formed entry %d, datalen:%d, " "start:%08x\n", cur_blk, data_len, start); continue; } image_size = data_len * block_size; image_start = (start + data_off) * block_size; is_dir = 2*(image_size < 800*1024); #if 0 printf(" partition add entry %d = %s %d %08x %08x\n", cur_blk, part_map_ptr->part_name, is_dir, image_size, image_start); #endif cfg_file_add_dirent(&g_cfg_partitionlist, part_map_ptr->part_name, is_dir, image_size, image_start, cur_blk); } free(blk_bufptr); return g_cfg_partitionlist.last; } int cfg_maybe_insert_disk(int slot, int drive, const char *namestr) { int num_parts; int fd; fd = open(namestr, O_RDONLY | O_BINARY, 0x1b6); if(fd < 0) { fatal_printf("Cannot open disk image: %s\n", namestr); return 0; } num_parts = cfg_partition_make_list(fd); close(fd); if(num_parts > 0) { printf("Choose a partition\n"); g_cfg_select_partition = 1; } else { insert_disk(slot, drive, namestr, 0, 0, 0, -1); return 1; } return 0; } int cfg_stat(char *path, struct stat *sb) { int removed_slash; int len; int ret; removed_slash = 0; len = 0; #ifdef _WIN32 /* Windows doesn't like to stat paths ending in a /, so remove it */ len = strlen(path); if((len > 1) && (path[len - 1] == '/') ) { path[len - 1] = 0; /* remove the slash */ removed_slash = 1; } #endif ret = stat(path, sb); #ifdef _WIN32 /* put the slash back */ if(removed_slash) { path[len - 1] = '/'; } #endif return ret; } void cfg_htab_vtab(int x, int y) { if(x > 79) { x = 0; } if(y > 23) { y = 0; } g_cfg_curs_x = x; g_cfg_curs_y = y; g_cfg_curs_inv = 0; g_cfg_curs_mousetext = 0; } void cfg_home() { int i; cfg_htab_vtab(0, 0); for(i = 0; i < 24; i++) { cfg_cleol(); } } void cfg_cleol() { g_cfg_curs_inv = 0; g_cfg_curs_mousetext = 0; cfg_putchar(' '); while(g_cfg_curs_x != 0) { cfg_putchar(' '); } } void cfg_putchar(int c) { int offset; int x, y; if(c == '\n') { cfg_cleol(); return; } if(c == '\b') { g_cfg_curs_inv = !g_cfg_curs_inv; return; } if(c == '\t') { g_cfg_curs_mousetext = !g_cfg_curs_mousetext; return; } y = g_cfg_curs_y; x = g_cfg_curs_x; offset = g_screen_index[g_cfg_curs_y]; if((x & 1) == 0) { offset += 0x10000; } if(g_cfg_curs_inv) { if(c >= 0x40 && c < 0x60) { c = c & 0x1f; } } else { c = c | 0x80; } if(g_cfg_curs_mousetext) { c = (c & 0x1f) | 0x40; } set_memory_c(0xe00400 + offset + (x >> 1), c, 0); x++; if(x >= 80) { x = 0; y++; if(y >= 24) { y = 0; } } g_cfg_curs_y = y; g_cfg_curs_x = x; } void cfg_printf(const char *fmt, ...) { va_list ap; int c; int i; va_start(ap, fmt); (void)vsnprintf(g_cfg_printf_buf, CFG_PRINTF_BUFSIZE, fmt, ap); va_end(ap); for(i = 0; i < CFG_PRINTF_BUFSIZE; i++) { c = g_cfg_printf_buf[i]; if(c == 0) { return; } cfg_putchar(c); } } void cfg_print_num(int num, int max_len) { char buf[64]; char buf2[64]; int len; int cnt; int c; int i, j; /* Prints right-adjusted "num" in field "max_len" wide */ snprintf(&buf[0], 64, "%d", num); len = strlen(buf); for(i = 0; i < 64; i++) { buf2[i] = ' '; } j = max_len + 1; buf2[j] = 0; j--; cnt = 0; for(i = len - 1; (i >= 0) && (j >= 1); i--) { c = buf[i]; if(c >= '0' && c <= '9') { if(cnt >= 3) { buf2[j--] = ','; cnt = 0; } cnt++; } buf2[j--] = c; } cfg_printf(&buf2[1]); } void cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras) { Disk *dsk; int slot, drive; slot = type_ext >> 8; drive = type_ext & 0xff; dsk = cfg_get_dsk_from_slot_drive(slot, drive); outstr[0] = 0; if(dsk->name_ptr == 0) { return; } config_generate_config_kegs_name(outstr, maxlen, dsk, with_extras); } void cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) { char valbuf[CFG_OPT_MAXSTR]; char **str_ptr; const char *menustr; char *curstr, *defstr; char *str; char *outstr; int *iptr; int val; int num_opts; int opt_num; int bufpos, outpos; int curval, defval; int type; int type_ext; int opt_get_str; int separator; int len; int c; int i; g_cfg_opt_buf[0] = 0; num_opts = 0; opt_get_str = 0; separator = ','; menuptr += menu_pos; /* move forward to entry menu_pos */ menustr = menuptr->str; type = menuptr->cfgtype; type_ext = (type >> 4); type = type & 0xf; len = strlen(menustr) + 1; bufpos = 0; outstr = &(g_cfg_opt_buf[0]); outstr[bufpos++] = ' '; outstr[bufpos++] = ' '; outstr[bufpos++] = '\t'; outstr[bufpos++] = '\t'; outstr[bufpos++] = ' '; outstr[bufpos++] = ' '; if(menu_pos == highlight_pos) { outstr[bufpos++] = '\b'; } opt_get_str = 2; i = -1; outpos = bufpos; #if 0 printf("cfg menu_pos: %d str len: %d\n", menu_pos, len); #endif while(++i < len) { c = menustr[i]; if(c == separator) { if(i == 0) { continue; } c = 0; } outstr[outpos++] = c; outstr[outpos] = 0; if(outpos >= CFG_OPT_MAXSTR) { fprintf(stderr, "CFG_OPT_MAXSTR exceeded\n"); my_exit(1); } if(c == 0) { if(opt_get_str == 2) { outstr = &(valbuf[0]); bufpos = outpos - 1; opt_get_str = 0; } else if(opt_get_str) { #if 0 if(menu_pos == highlight_pos) { printf("menu_pos %d opt %d = %s=%d\n", menu_pos, num_opts, g_cfg_opts_strs[num_opts], g_cfg_opts_vals[num_opts]); } #endif num_opts++; outstr = &(valbuf[0]); opt_get_str = 0; if(num_opts >= CFG_MAX_OPTS) { fprintf(stderr, "CFG_MAX_OPTS oflow\n"); my_exit(1); } } else { val = strtoul(valbuf, 0, 0); g_cfg_opts_vals[num_opts] = val; outstr = &(g_cfg_opts_strs[num_opts][0]); opt_get_str = 1; } outpos = 0; outstr[0] = 0; } } if(menu_pos == highlight_pos) { g_cfg_opt_buf[bufpos++] = '\b'; } g_cfg_opt_buf[bufpos] = 0; // Figure out if we should get a checkmark curval = -1; defval = -1; curstr = 0; if(type == CFGTYPE_INT) { iptr = menuptr->ptr; curval = *iptr; iptr = menuptr->defptr; defval = *iptr; if(curval == defval) { g_cfg_opt_buf[3] = 'D'; /* checkmark */ g_cfg_opt_buf[4] = '\t'; } } if(type == CFGTYPE_FILE) { str_ptr = (char **)menuptr->ptr; curstr = *str_ptr; str_ptr = (char **)menuptr->defptr; defstr = *str_ptr; if(strcmp(curstr,defstr) == 0) { g_cfg_opt_buf[3] = 'D'; /* checkmark */ g_cfg_opt_buf[4] = '\t'; } } // If it's a menu, give it a special menu indicator if(type == CFGTYPE_MENU) { g_cfg_opt_buf[1] = '\t'; g_cfg_opt_buf[2] = 'M'; /* return-like symbol */ g_cfg_opt_buf[3] = '\t'; g_cfg_opt_buf[4] = ' '; } // Decide what to display on the "right" side str = 0; opt_num = -1; if(type == CFGTYPE_INT || type == CFGTYPE_FILE) { g_cfg_opt_buf[bufpos++] = ' '; g_cfg_opt_buf[bufpos++] = '='; g_cfg_opt_buf[bufpos++] = ' '; g_cfg_opt_buf[bufpos] = 0; for(i = 0; i < num_opts; i++) { if(curval == g_cfg_opts_vals[i]) { opt_num = i; break; } } } if(change != 0) { if(type == CFGTYPE_INT) { if(num_opts > 0) { opt_num += change; if(opt_num >= num_opts) { opt_num = 0; } if(opt_num < 0) { opt_num = num_opts - 1; } curval = g_cfg_opts_vals[opt_num]; } else { curval += change; /* HACK: min_val, max_val testing here */ } iptr = (int *)menuptr->ptr; *iptr = curval; } g_config_kegs_update_needed = 1; } #if 0 if(menu_pos == highlight_pos) { printf("menu_pos %d opt_num %d\n", menu_pos, opt_num); } #endif if(opt_num >= 0) { str = &(g_cfg_opts_strs[opt_num][0]); } else { if(type == CFGTYPE_INT) { str = &(g_cfg_opts_strs[0][0]); snprintf(str, CFG_OPT_MAXSTR, "%d", curval); } else if (type == CFGTYPE_DISK) { str = &(g_cfg_opts_strs[0][0]), cfg_get_disk_name(str, CFG_OPT_MAXSTR, type_ext, 1); str = cfg_shorten_filename(str, 68); } else if (type == CFGTYPE_FILE) { str = &(g_cfg_opts_strs[0][0]); snprintf(str, CFG_OPT_MAXSTR, "%s", curstr); str = cfg_shorten_filename(str, 68); } else { str = ""; } } #if 0 if(menu_pos == highlight_pos) { printf("menu_pos %d buf_pos %d, str is %s, %02x, %02x, " "%02x %02x\n", menu_pos, bufpos, str, g_cfg_opt_buf[bufpos-1], g_cfg_opt_buf[bufpos-2], g_cfg_opt_buf[bufpos-3], g_cfg_opt_buf[bufpos-4]); } #endif g_cfg_opt_buf[bufpos] = 0; strncpy(&(g_cfg_opt_buf[bufpos]), str, CFG_OPT_MAXSTR - bufpos - 1); g_cfg_opt_buf[CFG_OPT_MAXSTR-1] = 0; } void cfg_get_base_path(char *pathptr, const char *inptr, int go_up) { const char *tmpptr; char *slashptr; char *outptr; int add_dotdot, is_dotdot; int len; int c; /* Take full filename, copy it to pathptr, and truncate at last slash */ /* inptr and pathptr can be the same */ /* if go_up is set, then replace a blank dir with ".." */ /* but first, see if path is currently just ../ over and over */ /* if so, just tack .. onto the end and return */ //printf("cfg_get_base start with %s\n", inptr); g_cfg_file_match[0] = 0; tmpptr = inptr; is_dotdot = 1; while(1) { if(tmpptr[0] == 0) { break; } if(tmpptr[0] == '.' && tmpptr[1] == '.' && tmpptr[2] == '/') { tmpptr += 3; } else { is_dotdot = 0; break; } } slashptr = 0; outptr = pathptr; c = -1; while(c != 0) { c = *inptr++; if(c == '/') { if(*inptr != 0) { /* if not a trailing slash... */ slashptr = outptr; } } *outptr++ = c; } if(!go_up) { /* if not go_up, copy chopped part to g_cfg_file_match*/ /* copy from slashptr+1 to end */ tmpptr = slashptr+1; if(slashptr == 0) { tmpptr = pathptr; } strncpy(&g_cfg_file_match[0], tmpptr, CFG_PATH_MAX); /* remove trailing / from g_cfg_file_match */ len = strlen(&g_cfg_file_match[0]); if((len > 1) && (len < (CFG_PATH_MAX - 1)) && g_cfg_file_match[len - 1] == '/') { g_cfg_file_match[len - 1] = 0; } //printf("set g_cfg_file_match to %s\n", &g_cfg_file_match[0]); } if(!is_dotdot && (slashptr != 0)) { slashptr[0] = '/'; slashptr[1] = 0; outptr = slashptr + 2; } add_dotdot = 0; if(pathptr[0] == 0 || is_dotdot) { /* path was blank, or consisted of just ../ pattern */ if(go_up) { add_dotdot = 1; } } else if(slashptr == 0) { /* no slashes found, but path was not blank--make it blank */ if(pathptr[0] == '/') { pathptr[1] = 0; } else { pathptr[0] = 0; } } if(add_dotdot) { --outptr; outptr[0] = '.'; outptr[1] = '.'; outptr[2] = '/'; outptr[3] = 0; } //printf("cfg_get_base end with %s, is_dotdot:%d, add_dotdot:%d\n", // pathptr, is_dotdot, add_dotdot); } void cfg_file_init() { int slot, drive; int i; if(g_cfg_slotdrive < 0xfff) { cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, g_cfg_slotdrive, 0); slot = g_cfg_slotdrive >> 8; drive = g_cfg_slotdrive & 1; for(i = 0; i < 6; i++) { if(g_cfg_tmp_path[0] != 0) { break; } /* try to get a starting path from some mounted drive */ drive = !drive; if(i & 1) { slot++; if(slot >= 8) { slot = 5; } } cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, (slot << 8) + drive, 0); } } else { // Just use g_cfg_file_def_name strncpy(&g_cfg_tmp_path[0], g_cfg_file_def_name, CFG_PATH_MAX); } cfg_get_base_path(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0], 0); g_cfg_dirlist.invalid = 1; } void cfg_free_alldirents(Cfg_listhdr *listhdrptr) { int i; if(listhdrptr->max > 0) { // Free the old directory listing for(i = 0; i < listhdrptr->last; i++) { free(listhdrptr->direntptr[i].name); } free(listhdrptr->direntptr); } listhdrptr->direntptr = 0; listhdrptr->last = 0; listhdrptr->max = 0; listhdrptr->invalid = 0; listhdrptr->topent = 0; listhdrptr->curent = 0; } void cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, int size, int image_start, int part_num) { Cfg_dirent *direntptr; char *ptr; int inc_amt; int namelen; namelen = strlen(nameptr); if(listhdrptr->last >= listhdrptr->max) { // realloc inc_amt = MAX(64, listhdrptr->max); inc_amt = MIN(inc_amt, 1024); listhdrptr->max += inc_amt; listhdrptr->direntptr = realloc(listhdrptr->direntptr, listhdrptr->max * sizeof(Cfg_dirent)); } ptr = malloc(namelen+1+is_dir); strncpy(ptr, nameptr, namelen+1); if(is_dir) { strcat(ptr, "/"); } #if 0 printf("...file entry %d is %s\n", g_cfg_dirlist.last, ptr); #endif direntptr = &(listhdrptr->direntptr[listhdrptr->last]); direntptr->name = ptr; direntptr->is_dir = is_dir; direntptr->size = size; direntptr->image_start = image_start; direntptr->part_num = part_num; listhdrptr->last++; } int cfg_dirent_sortfn(const void *obj1, const void *obj2) { const Cfg_dirent *direntptr1, *direntptr2; int ret; /* Called by qsort to sort directory listings */ direntptr1 = (const Cfg_dirent *)obj1; direntptr2 = (const Cfg_dirent *)obj2; #if defined(MAC) || defined(_WIN32) ret = strcasecmp(direntptr1->name, direntptr2->name); #else ret = strcmp(direntptr1->name, direntptr2->name); #endif return ret; } int cfg_str_match(const char *str1, const char *str2, int len) { const byte *bptr1, *bptr2; int c, c2; int i; /* basically, work like strcmp, except if str1 ends first, return 0 */ bptr1 = (const byte *)str1; bptr2 = (const byte *)str2; for(i = 0; i < len; i++) { c = *bptr1++; c2 = *bptr2++; if(c == 0) { if(i > 0) { return 0; } else { return c - c2; } } if(c != c2) { return c - c2; } } return 0; } void cfg_file_readdir(const char *pathptr) { struct dirent *direntptr; struct stat stat_buf; DIR *dirptr; mode_t fmt; char *str; const char *tmppathptr; int size; int ret; int is_dir, is_gz; int len; int i; if(!strncmp(pathptr, &g_cfg_file_cachedpath[0], CFG_PATH_MAX) && (g_cfg_dirlist.last > 0) && (g_cfg_dirlist.invalid==0)){ return; } // No match, must read new directory // Free all dirents that were cached previously cfg_free_alldirents(&g_cfg_dirlist); strncpy(&g_cfg_file_cachedpath[0], pathptr, CFG_PATH_MAX); strncpy(&g_cfg_file_cachedreal[0], pathptr, CFG_PATH_MAX); str = &g_cfg_file_cachedreal[0]; for(i = 0; i < 200; i++) { len = strlen(str); if(len <= 0) { break; } else if(len < CFG_PATH_MAX-2) { if(str[len-1] != '/') { // append / to make various routines work str[len] = '/'; str[len+1] = 0; } } ret = cfg_stat(str, &stat_buf); is_dir = 0; if(ret == 0) { fmt = stat_buf.st_mode & S_IFMT; if(fmt == S_IFDIR) { /* it's a directory */ is_dir = 1; } } if(is_dir) { break; } else { // user is entering more path, use base for display cfg_get_base_path(str, str, 0); } } tmppathptr = str; if(str[0] == 0) { tmppathptr = "."; } cfg_file_add_dirent(&g_cfg_dirlist, "..", 1, 0, -1, -1); dirptr = opendir(tmppathptr); if(dirptr == 0) { printf("Could not open %s as a directory\n", tmppathptr); return; } while(1) { direntptr = readdir(dirptr); if(direntptr == 0) { break; } if(!strcmp(".", direntptr->d_name)) { continue; } if(!strcmp("..", direntptr->d_name)) { continue; } /* Else, see if it is a directory or a file */ snprintf(&g_cfg_tmp_path[0], CFG_PATH_MAX, "%s%s", &g_cfg_file_cachedreal[0], direntptr->d_name); ret = cfg_stat(&g_cfg_tmp_path[0], &stat_buf); len = strlen(g_cfg_tmp_path); is_dir = 0; is_gz = 0; if((len > 3) && (strcmp(&g_cfg_tmp_path[len - 3], ".gz") == 0)){ is_gz = 1; } if(ret != 0) { printf("stat %s ret %d, errno:%d\n", &g_cfg_tmp_path[0], ret, errno); stat_buf.st_size = 0; continue; /* skip it */ } else { fmt = stat_buf.st_mode & S_IFMT; size = stat_buf.st_size; if(fmt == S_IFDIR) { /* it's a directory */ is_dir = 1; } else if((fmt == S_IFREG) && (is_gz == 0)) { if(g_cfg_slotdrive < 0xfff) { if(size < 140*1024) { continue; /* skip it */ } } else { /* see if there are size limits */ if((size < g_cfg_file_min_size) || (size > g_cfg_file_max_size)) { continue; /* skip it */ } } } } cfg_file_add_dirent(&g_cfg_dirlist, direntptr->d_name, is_dir, stat_buf.st_size, -1, -1); } /* then sort the results (Mac's HFS+ is sorted, but other FS won't be)*/ qsort(&(g_cfg_dirlist.direntptr[0]), g_cfg_dirlist.last, sizeof(Cfg_dirent), cfg_dirent_sortfn); g_cfg_dirlist.curent = g_cfg_dirlist.last - 1; for(i = g_cfg_dirlist.last - 1; i >= 0; i--) { ret = cfg_str_match(&g_cfg_file_match[0], g_cfg_dirlist.direntptr[i].name, CFG_PATH_MAX); if(ret <= 0) { /* set cur ent to closest filename to the match name */ g_cfg_dirlist.curent = i; } } } char * cfg_shorten_filename(const char *in_ptr, int maxlen) { char *out_ptr; int len; int c; int i; /* Warning: uses a static string, not reentrant! */ out_ptr = &(g_cfg_file_shortened[0]); len = strlen(in_ptr); maxlen = MIN(len, maxlen); for(i = 0; i < maxlen; i++) { c = in_ptr[i] & 0x7f; if(c < 0x20) { c = '*'; } out_ptr[i] = c; } out_ptr[maxlen] = 0; if(len > maxlen) { for(i = 0; i < (maxlen/2); i++) { c = in_ptr[len-i-1] & 0x7f; if(c < 0x20) { c = '*'; } out_ptr[maxlen-i-1] = c; } out_ptr[(maxlen/2) - 1] = '.'; out_ptr[maxlen/2] = '.'; out_ptr[(maxlen/2) + 1] = '.'; } return out_ptr; } void cfg_fix_topent(Cfg_listhdr *listhdrptr) { int num_to_show; num_to_show = listhdrptr->num_to_show; /* Force curent and topent to make sense */ if(listhdrptr->curent >= listhdrptr->last) { listhdrptr->curent = listhdrptr->last - 1; } if(listhdrptr->curent < 0) { listhdrptr->curent = 0; } if(abs(listhdrptr->curent - listhdrptr->topent) >= num_to_show) { listhdrptr->topent = listhdrptr->curent - (num_to_show/2); } if(listhdrptr->topent > listhdrptr->curent) { listhdrptr->topent = listhdrptr->curent - (num_to_show/2); } if(listhdrptr->topent < 0) { listhdrptr->topent = 0; } } void cfg_file_draw() { Cfg_listhdr *listhdrptr; Cfg_dirent *direntptr; char *str, *fmt; int num_to_show; int yoffset; int x, y; int i; cfg_file_readdir(&g_cfg_file_curpath[0]); for(y = 0; y < 21; y++) { cfg_htab_vtab(0, y); cfg_printf("\tZ\t"); for(x = 1; x < 79; x++) { cfg_htab_vtab(x, y); cfg_putchar(' '); } cfg_htab_vtab(79, y); cfg_printf("\t_\t"); } cfg_htab_vtab(1, 0); cfg_putchar('\b'); for(x = 1; x < 79; x++) { cfg_putchar(' '); } if(g_cfg_slotdrive < 0xfff) { cfg_htab_vtab(30, 0); cfg_printf("\bSelect image for s%dd%d\b", (g_cfg_slotdrive >> 8), (g_cfg_slotdrive & 0xff) + 1); } else { cfg_htab_vtab(5, 0); cfg_printf("\bSelect file to use as %-40s\b", cfg_shorten_filename(g_cfg_file_def_name, 40)); } cfg_htab_vtab(2, 1); cfg_printf("config.kegs path: %-56s", cfg_shorten_filename(&g_config_kegs_name[0], 56)); cfg_htab_vtab(2, 2); cfg_printf("Current KEGS directory: %-50s", cfg_shorten_filename(&g_cfg_cwd_str[0], 50)); cfg_htab_vtab(2, 3); str = ""; if(g_cfg_file_pathfield) { str = "\b \b"; } cfg_printf("Path: %s%s", cfg_shorten_filename(&g_cfg_file_curpath[0], 68), str); cfg_htab_vtab(0, 4); cfg_printf(" \t"); for(x = 1; x < 79; x++) { cfg_putchar('\\'); } cfg_printf("\t "); /* Force curent and topent to make sense */ listhdrptr = &g_cfg_dirlist; num_to_show = CFG_NUM_SHOWENTS; yoffset = 5; if(g_cfg_select_partition > 0) { listhdrptr = &g_cfg_partitionlist; num_to_show -= 2; cfg_htab_vtab(2, yoffset); cfg_printf("Select partition of %-50s\n", cfg_shorten_filename(&g_cfg_file_path[0], 50), str); yoffset += 2; } listhdrptr->num_to_show = num_to_show; cfg_fix_topent(listhdrptr); for(i = 0; i < num_to_show; i++) { y = i + yoffset; if(listhdrptr->last > (i + listhdrptr->topent)) { direntptr = &(listhdrptr-> direntptr[i + listhdrptr->topent]); cfg_htab_vtab(2, y); if(direntptr->is_dir) { cfg_printf("\tXY\t "); } else { cfg_printf(" "); } if(direntptr->part_num >= 0) { cfg_printf("%3d: ", direntptr->part_num); } str = cfg_shorten_filename(direntptr->name, 45); fmt = "%-45s"; if(i + listhdrptr->topent == listhdrptr->curent) { if(g_cfg_file_pathfield == 0) { fmt = "\b%-45s\b"; } else { fmt = "%-44s\b \b"; } } cfg_printf(fmt, str); if(!direntptr->is_dir) { cfg_print_num(direntptr->size, 13); } } } cfg_htab_vtab(1, 5 + CFG_NUM_SHOWENTS); cfg_putchar('\t'); for(x = 1; x < 79; x++) { cfg_putchar('L'); } cfg_putchar('\t'); } void cfg_partition_selected() { char *str; const char *part_str; char *part_str2; int pos; int part_num; pos = g_cfg_partitionlist.curent; str = g_cfg_partitionlist.direntptr[pos].name; part_num = -2; part_str = 0; if(str[0] == 0 || (str[0] >= '0' && str[0] <= '9')) { part_num = g_cfg_partitionlist.direntptr[pos].part_num; } else { part_str = str; } part_str2 = 0; if(part_str != 0) { part_str2 = (char *)malloc(strlen(part_str)+1); strcpy(part_str2, part_str); } insert_disk(g_cfg_slotdrive >> 8, g_cfg_slotdrive & 0xff, &(g_cfg_file_path[0]), 0, 0, part_str2, part_num); if(part_str2 != 0) { free(part_str2); } g_cfg_slotdrive = -1; g_cfg_select_partition = -1; } void cfg_file_update_ptr(char *str) { char *newstr; int len; len = strlen(str) + 1; newstr = malloc(len); memcpy(newstr, str, len); if(g_cfg_file_strptr) { if(*g_cfg_file_strptr) { free(*g_cfg_file_strptr); } } *g_cfg_file_strptr = newstr; if(g_cfg_file_strptr == &(g_cfg_rom_path)) { printf("Updated ROM file\n"); load_roms_init_memory(); } g_config_kegs_update_needed = 1; } void cfg_file_selected() { struct stat stat_buf; char *str; int fmt; int ret; if(g_cfg_select_partition > 0) { cfg_partition_selected(); return; } if(g_cfg_file_pathfield == 0) { // in file section area of window str = g_cfg_dirlist.direntptr[g_cfg_dirlist.curent].name; if(!strcmp(str, "../")) { /* go up one directory */ cfg_get_base_path(&g_cfg_file_curpath[0], &g_cfg_file_curpath[0], 1); return; } snprintf(&g_cfg_file_path[0], CFG_PATH_MAX, "%s%s", &g_cfg_file_cachedreal[0], str); } else { // just use cfg_file_curpath directly strncpy(&g_cfg_file_path[0], &g_cfg_file_curpath[0], CFG_PATH_MAX); } ret = cfg_stat(&g_cfg_file_path[0], &stat_buf); fmt = stat_buf.st_mode & S_IFMT; cfg_printf("Stat'ing %s, st_mode is: %08x\n", &g_cfg_file_path[0], (int)stat_buf.st_mode); if(ret != 0) { printf("stat %s returned %d, errno: %d\n", &g_cfg_file_path[0], ret, errno); } else { if(fmt == S_IFDIR) { /* it's a directory */ strncpy(&g_cfg_file_curpath[0], &g_cfg_file_path[0], CFG_PATH_MAX); } else { /* select it */ if(g_cfg_slotdrive < 0xfff) { ret = cfg_maybe_insert_disk(g_cfg_slotdrive>>8, g_cfg_slotdrive & 0xff, &g_cfg_file_path[0]); if(ret > 0) { g_cfg_slotdrive = -1; } } else { cfg_file_update_ptr(&g_cfg_file_path[0]); g_cfg_slotdrive = -1; } } } } void cfg_file_handle_key(int key) { Cfg_listhdr *listhdrptr; int len; if(g_cfg_file_pathfield) { if(key >= 0x20 && key < 0x7f) { len = strlen(&g_cfg_file_curpath[0]); if(len < CFG_PATH_MAX-4) { g_cfg_file_curpath[len] = key; g_cfg_file_curpath[len+1] = 0; } return; } } listhdrptr = &g_cfg_dirlist; if(g_cfg_select_partition > 0) { listhdrptr = &g_cfg_partitionlist; } if( (g_cfg_file_pathfield == 0) && ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z')) ) { /* jump to file starting with this letter */ g_cfg_file_match[0] = key; g_cfg_file_match[1] = 0; g_cfg_dirlist.invalid = 1; /* re-read directory */ } switch(key) { case 0x1b: if(g_cfg_slotdrive < 0xfff) { eject_disk_by_num(g_cfg_slotdrive >> 8, g_cfg_slotdrive & 0xff); } g_cfg_slotdrive = -1; g_cfg_select_partition = -1; g_cfg_dirlist.invalid = 1; break; case 0x0a: /* down arrow */ if(g_cfg_file_pathfield == 0) { listhdrptr->curent++; cfg_fix_topent(listhdrptr); } break; case 0x0b: /* up arrow */ if(g_cfg_file_pathfield == 0) { listhdrptr->curent--; cfg_fix_topent(listhdrptr); } break; case 0x0d: /* return */ printf("handling return press\n"); cfg_file_selected(); break; case 0x09: /* tab */ g_cfg_file_pathfield = !g_cfg_file_pathfield; break; case 0x08: /* left arrow */ case 0x7f: /* delete key */ if(g_cfg_file_pathfield) { // printf("left arrow/delete\n"); len = strlen(&g_cfg_file_curpath[0]) - 1; if(len >= 0) { g_cfg_file_curpath[len] = 0; } } break; default: printf("key: %02x\n", key); } #if 0 printf("curent: %d, topent: %d, last: %d\n", g_cfg_dirlist.curent, g_cfg_dirlist.topent, g_cfg_dirlist.last); #endif } void config_control_panel() { void (*fn_ptr)(); const char *str; Cfg_menu *menuptr; void *ptr; int print_eject_help; int line; int type; int match_found; int menu_line; int menu_inc; int max_line; int key; int i, j; // First, save important text screen state g_save_cur_a2_stat = g_cur_a2_stat; for(i = 0; i < 0x400; i++) { g_save_text_screen_bytes[i] = g_slow_memory_ptr[0x400+i]; g_save_text_screen_bytes[0x400+i] =g_slow_memory_ptr[0x10400+i]; } g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_VID80 | ALL_STAT_ANNUNC3 | (0xf << BIT_ALL_STAT_TEXT_COLOR) | ALL_STAT_ALTCHARSET; g_a2_new_all_stat[0] = g_cur_a2_stat; g_new_a2_stat_cur_line = 0; cfg_printf("In config_control_panel\n"); for(i = 0; i < 20; i++) { // Toss any queued-up keypresses if(adb_read_c000() & 0x80) { (void)adb_access_c010(); } } g_adb_repeat_vbl = 0; g_cfg_vbl_count = 0; // HACK: Force adb keyboard (and probably mouse) to "normal"... g_full_refresh_needed = -1; g_a2_screen_buffer_changed = -1; cfg_home(); j = 0; menuptr = g_cfg_main_menu; if(g_rom_version < 0) { /* Must select ROM file */ menuptr = g_cfg_rom_menu; } menu_line = 1; menu_inc = 1; g_cfg_slotdrive = -1; g_cfg_select_partition = -1; while(g_config_control_panel) { if(g_fatal_log > 0) { x_show_alert(0, 0); } cfg_home(); line = 1; max_line = 1; match_found = 0; print_eject_help = 0; cfg_printf("%s\n\n", menuptr[0].str); while(line < 24) { str = menuptr[line].str; type = menuptr[line].cfgtype; ptr = menuptr[line].ptr; if(str == 0) { break; } if((type & 0xf) == CFGTYPE_DISK) { print_eject_help = 1; } cfg_parse_menu(menuptr, line, menu_line, 0); if(line == menu_line) { if(type != 0) { match_found = 1; } else if(menu_inc) { menu_line++; } else { menu_line--; } } if(line > max_line) { max_line = line; } cfg_printf("%s\n", g_cfg_opt_buf); line++; } if((menu_line < 1) && !match_found) { menu_line = 1; } if((menu_line >= max_line) && !match_found) { menu_line = max_line; } if(g_rom_version < 0) { cfg_htab_vtab(0, 21); cfg_printf("\bYOU MUST SELECT A VALID ROM FILE\b\n"); } cfg_htab_vtab(0, 23); cfg_printf("Move: \tJ\t \tK\t Change: \tH\t \tU\t \tM\t"); if(print_eject_help) { cfg_printf(" Eject: "); if(g_cfg_slotdrive >= 0) { cfg_printf("\bESC\b"); } else { cfg_printf("E"); } } #if 0 cfg_htab_vtab(0, 22); cfg_printf("menu_line: %d line: %d, vbl:%d, adb:%d key_dn:%d\n", menu_line, line, g_cfg_vbl_count, g_adb_repeat_vbl, g_key_down); #endif if(g_cfg_slotdrive >= 0) { cfg_file_draw(); } key = -1; while(g_config_control_panel) { video_update(); key = adb_read_c000(); if(key & 0x80) { key = key & 0x7f; (void)adb_access_c010(); break; } else { key = -1; } micro_sleep(1.0/60.0); g_cfg_vbl_count++; if(!match_found) { break; } } if((key >= 0) && (g_cfg_slotdrive < 0)) { // Normal menu system switch(key) { case 0x0a: /* down arrow */ menu_line++; menu_inc = 1; break; case 0x0b: /* up arrow */ menu_line--; menu_inc = 0; if(menu_line < 1) { menu_line = 1; } break; case 0x15: /* right arrow */ cfg_parse_menu(menuptr, menu_line,menu_line,1); break; case 0x08: /* left arrow */ cfg_parse_menu(menuptr,menu_line,menu_line,-1); break; case 0x0d: type = menuptr[menu_line].cfgtype; ptr = menuptr[menu_line].ptr; switch(type & 0xf) { case CFGTYPE_MENU: menuptr = (Cfg_menu *)ptr; menu_line = 1; break; case CFGTYPE_DISK: g_cfg_slotdrive = type >> 4; cfg_file_init(); break; case CFGTYPE_FUNC: fn_ptr = (void (*)())ptr; (*fn_ptr)(); break; case CFGTYPE_FILE: g_cfg_slotdrive = 0xfff; g_cfg_file_def_name = *((char **)ptr); g_cfg_file_strptr = (char **)ptr; cfg_file_init(); } break; case 0x1b: // Jump to last menu entry menu_line = max_line; break; case 'e': case 'E': type = menuptr[menu_line].cfgtype; if((type & 0xf) == CFGTYPE_DISK) { eject_disk_by_num(type >> 12, (type >> 4) & 0xff); } break; default: printf("key: %02x\n", key); } } else if(key >= 0) { cfg_file_handle_key(key); } } for(i = 0; i < 0x400; i++) { set_memory_c(0xe00400+i, g_save_text_screen_bytes[i], 0); set_memory_c(0xe10400+i, g_save_text_screen_bytes[0x400+i], 0); } // And quit g_config_control_panel = 0; g_adb_repeat_vbl = g_vbl_count + 60; g_cur_a2_stat = g_save_cur_a2_stat; change_display_mode(g_cur_dcycs); g_full_refresh_needed = -1; g_a2_screen_buffer_changed = -1; }