//	bfe2 - memory dump
//	Copyright (c) 1999-2003 Brand Huntsman and Lee Salzman
//


#include "common.h"
#include "functions.h"


//////////////////////////////////////////////////////////////////////////

// global
s_memwin *memwin_head, *memwin_tail;

// local
GtkWidget *mem_address_text, *mem_size_text, *mem_dump_button;
s_bgroup *mem_type, *mem_size, *mem_format;

#define MEM_COLUMN_TITLES 4
gchar *mem_column_titles[] = { "Base", "Offset", "Values", "Characters" };

char *mt_titles[] = { " Physical ", " Linear " };
char *ms_titles[] = { " Byte ", " Word ", " DoubleWord ", " QuadWord " };
char *mf_titles[] = { " Hex ", " Decimal ", " Unsigned Decimal ", " Binary ", " Octal " };

//////////////////////////////////////////////////////////////////////////


void refresh_memory( GtkWidget *widget, gpointer data ){
	memoryUpdateWindow((s_memwin *)data);
}


void m_destroy_window( GtkWidget *widget, gpointer data ){
	s_memwin *m = (s_memwin *)data;
	gtk_signal_disconnect(GTK_OBJECT(m->window), m->id);
	gtk_widget_destroy(m->window);


	if(memwin_head == m){
		memwin_head = m->next;
		if(memwin_head) memwin_head->prev = NULL;
		if(memwin_tail == m) memwin_tail = NULL;
	} else if(memwin_tail == m){
		memwin_tail = m->prev;
		if(memwin_tail) memwin_tail->next = NULL;
	} else {
		m->prev->next = m->next;
		m->next->prev = m->prev;
	}
	free(m);
}
#define LEN_MEMWIN_TITLE 64
void open_memory( GtkWidget *widget, gint row, gint column, GdkEventButton *event, gpointer data ){
	GtkWidget *vbox, *hbox;
	char title[LEN_MEMWIN_TITLE];
	s_memwin *m;
	int32 base, units;

	if(sscanf(gtk_entry_get_text(GTK_ENTRY(mem_address_text)), "%x", &base) != 1) return;
	if(sscanf(gtk_entry_get_text(GTK_ENTRY(mem_size_text)), "%u", &units) != 1) return;

	// create memory structure
	if((m = (s_memwin *)malloc(sizeof(s_memwin))) == NULL) return;

	// add to list
	m->next = NULL;
	if(memwin_head == NULL){
		memwin_head = m;
	} else {
		m->prev = memwin_tail;
		memwin_tail->next = m;
	}
	memwin_tail = m;

	// get values from memory page
	m->base = base;
	m->nr_units = units;
	m->unit_type = mem_type->current;
	m->unit_size = mem_size->current;
	m->unit_format = mem_format->current;

	gtk_entry_set_text(GTK_ENTRY(mem_address_text), "");
	gtk_entry_set_text(GTK_ENTRY(mem_size_text), "");

	// create window
	if(mem_type == MT_PHYSICAL)
		snprintf(title, LEN_MEMWIN_TITLE, "BFE - Memory Dump - %.8X physical", m->base);
	else snprintf(title, LEN_MEMWIN_TITLE, "BFE - Memory Dump - %.8X linear", m->base);
	m->window = new_window(GTK_WINDOW_TOPLEVEL, title, 550, 300);
	m->id = gtk_signal_connect(GTK_OBJECT(m->window), "destroy", GTK_SIGNAL_FUNC(m_destroy_window), (gpointer)m);
	vbox = new_window_vbox(m->window);

	// create list
	if(m->unit_size == MS_BYTE){
		// character memory
		m->list = new_list(vbox, MEM_COLUMN_TITLES, mem_column_titles);
		gtk_clist_set_column_justification(m->list, 0, GTK_JUSTIFY_CENTER);
		gtk_clist_set_column_justification(m->list, 1, GTK_JUSTIFY_RIGHT);
		gtk_clist_set_column_justification(m->list, 2, GTK_JUSTIFY_LEFT);
		gtk_clist_set_column_justification(m->list, 3, GTK_JUSTIFY_LEFT);
	} else {
		// non-character memory
		m->list = new_list(vbox, MEM_COLUMN_TITLES-1, mem_column_titles);
		gtk_clist_set_column_justification(m->list, 0, GTK_JUSTIFY_CENTER);
		gtk_clist_set_column_justification(m->list, 1, GTK_JUSTIFY_RIGHT);
		gtk_clist_set_column_justification(m->list, 2, GTK_JUSTIFY_LEFT);
	}
	gtk_clist_column_titles_show(m->list);

	hbox = new_hbox(vbox, FALSE);

	new_gap(hbox);

	m->refresh_button = new_button(hbox, TRUE, " Refresh ");
	gtk_signal_connect(GTK_OBJECT(m->refresh_button), "clicked", GTK_SIGNAL_FUNC(refresh_memory), (gpointer)m);

	m->close_button = new_button(hbox, FALSE, " Close ");
	gtk_signal_connect(GTK_OBJECT(m->close_button), "clicked", GTK_SIGNAL_FUNC(m_destroy_window), (gpointer)m);

	memoryUpdateWindow(m);
}


//////////////////////////////////////////////////////////////////////////


void memoryInit( GtkWidget *vbox ){
	GtkWidget *hbox;

	hbox = new_hbox(vbox, FALSE);
	new_label(hbox, FALSE, "Address (hex)", FALSE);
	mem_address_text = new_text_entry(hbox, FALSE, 100);
	gtk_entry_set_max_length(GTK_ENTRY(mem_address_text), 10);

	mem_type = new_button_group(hbox, FALSE, HORIZONTAL, 2, mt_titles);

	/////////////////////////////////////////////

	hbox = new_hbox(vbox, FALSE);
	new_label(hbox, FALSE, "Units (dec)", FALSE);
	mem_size_text = new_text_entry(hbox, FALSE, 100);
	gtk_entry_set_max_length(GTK_ENTRY(mem_size_text), 5);
	gtk_signal_connect(GTK_OBJECT(mem_size_text), "activate", GTK_SIGNAL_FUNC(open_memory), NULL);

	mem_dump_button = new_button(hbox, FALSE, " Dump ");
	gtk_widget_set_usize(mem_dump_button, 100, WIDGET_HEIGHT);
	gtk_signal_connect(GTK_OBJECT(mem_dump_button), "clicked", GTK_SIGNAL_FUNC(open_memory), NULL);

	/////////////////////////////////////////////

	hbox = new_hbox(vbox, FALSE);
	new_label(hbox, FALSE, "Unit Size", FALSE);

	mem_size = new_button_group(hbox, FALSE, HORIZONTAL, 4, ms_titles);

	/////////////////////////////////////////////

	hbox = new_hbox(vbox, FALSE);
	new_label(hbox, FALSE, "Unit Format", FALSE);

	mem_format = new_button_group(hbox, FALSE, HORIZONTAL, 5, mf_titles);

	memwin_head = memwin_tail = NULL;
}


#define LEN_VALUES 60
#define LEN_CHARS 17
void memoryUpdateWindow( s_memwin *m ){
	char base[LEN_ADDRESS], offset[LEN_NUMBER], values[LEN_VALUES], characters[LEN_CHARS], *row[MEM_COLUMN_TITLES];
	char buffer[LEN_BUFFER], *word, c, *s = buffer;
	char *sizes = "bhwg", *formats = "xduto";
	uint i;
	uint32 base_num;

	if(bochs_offline) return;

	row[0] = base;
	row[1] = offset;
	row[2] = values;
	if(m->unit_size == MS_BYTE) row[3] = characters;
	else row[3] = NULL;

	gtk_clist_freeze(m->list);
	gtk_clist_clear(m->list);

	if(m->unit_type == MT_PHYSICAL){
		fprintf(writepipe, "xp /%u%c%c 0x%X\n",
			m->nr_units, sizes[m->unit_size], formats[m->unit_format], m->base);
	} else {
		fprintf(writepipe, "x /%u%c%c 0x%X\n",
			m->nr_units, sizes[m->unit_size], formats[m->unit_format], m->base);
	}

	// first line has a space before it
	fgetc(readpipe);

	for(;;){
		c = fgetc(readpipe);
		if(c == '\n'){
			*s = '\0';
			i = 0;

			get_word(buffer, &i, &word);
			sscanf(word, "%x", &base_num);
			snprintf(base, LEN_ADDRESS, "%.8X", base_num);

			get_word(buffer, &i, &word);
			strncpy(offset, word, LEN_NUMBER);

			row[2] = &buffer[i];

			if(m->unit_size == MS_BYTE) characters[0] = '\0';

			if(offset[0] == 'd'){			// bochs doesn't support quadwords
				base[0] = '\0';
				offset[0] = '\0';
			} else if(offset[0] == 'r'){		// zero units
				base[0] = '\0';
				offset[0] = '\0';
			}

			gtk_clist_append(m->list, row);
			s = buffer;
		} else if(c == '<'){
			if(s == buffer){
				fscanf(readpipe, "bochs:%u>", &p_step);
				break;
			} else {
				fscanf(readpipe, "bogus");
			}
		} else if(c == '>'){
			fgetc(readpipe);
		} else if(c == EOF){
			break;
		} else if(c == ' ' || c == '\t'){
			if(s[-1] != '\t'){
				*s = ' ';
				s++;
			}
		} else {
			*s = c;
			s++;
		}

  // TODO: don't let s exceed buffer+LEN_BUFFER

	}
	gtk_clist_thaw(m->list);
}


void memoryEnablePage( gboolean v ){
	s_memwin *m;

	gtk_widget_set_sensitive(mem_address_text, v);
	gtk_widget_set_sensitive(mem_size_text, v);
	gtk_widget_set_sensitive(mem_dump_button, v);
	for(m = memwin_head; m != NULL; m = m->next)
		if(m->window) gtk_widget_set_sensitive(m->refresh_button, v);
}
