kegs/src/config.c

3056 lines
69 KiB
C

/************************************************************************/
/* 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 <stdarg.h>
#include "config.h"
#include <dirent.h>
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;
}