// bootdial.cpp : implementation file
//

#include "stdafx.h"
#include "winboot.h"
#include "winbodoc.h"
#include "bootdial.h"
#include "bsdselec.h"

#include "reboot.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CBootDialog

IMPLEMENT_DYNCREATE(CBootDialog, CFormView)

CBootDialog::CBootDialog()
	: CFormView(CBootDialog::IDD)
{
	//{{AFX_DATA_INIT(CBootDialog)
	m_askfor = FALSE;
	m_cdrom = FALSE;
	m_dos = FALSE;
	m_kdb = FALSE;
	m_rootdev = FALSE;
	m_singleu = FALSE;
	m_uconf = FALSE;
	m_verbose = FALSE;
	m_kernel = "";
	//}}AFX_DATA_INIT
}

CBootDialog::~CBootDialog()
{
}

void CBootDialog::DoDataExchange(CDataExchange* pDX)
{
	CFormView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CBootDialog)
	DDX_Control(pDX, IDC_SEARCH, m_searchButton);
	DDX_Check(pDX, IDC_ASKFOR, m_askfor);
	DDX_Check(pDX, IDC_CDROMROOT, m_cdrom);
	DDX_Check(pDX, IDC_DOS, m_dos);
	DDX_Check(pDX, IDC_KDB, m_kdb);
	DDX_Check(pDX, IDC_ROOTDEV, m_rootdev);
	DDX_Check(pDX, IDC_SINGLEU, m_singleu);
	DDX_Check(pDX, IDC_UCONF, m_uconf);
	DDX_Check(pDX, IDC_VERBOSE, m_verbose);
	DDX_Text(pDX, IDC_EDIT1, m_kernel);
	DDV_MaxChars(pDX, m_kernel, 512);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CBootDialog, CFormView)
	//{{AFX_MSG_MAP(CBootDialog)
	ON_BN_CLICKED(IDBOOT, OnBoot)
	ON_BN_CLICKED(IDC_SEARCH, OnSearch)
	ON_BN_CLICKED(IDCANCEL, OnCancel)
	ON_BN_CLICKED(IDC_DOS, OnDos)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CBootDialog message handlers

#ifdef _DEBUG
CWinbootDoc* CBootDialog::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CWinbootDoc)));
	return (CWinbootDoc*)m_pDocument;
}
#endif //_DEBUG


void CBootDialog::OnInitialUpdate()
{
	GetParentFrame()->RecalcLayout();
	ResizeParentToFit(FALSE);
	UpdateData(FALSE);
}

void CBootDialog::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
	WORD flags = GetDocument()->m_bootflags;

	m_askfor = flags & RB_ASKNAME;
	m_cdrom = flags & RB_CDROM;
	m_dos = GetDocument()->m_bootdos;
	m_kdb = flags & RB_KDB;
	m_rootdev = flags & RB_DFLTROOT;
	m_singleu = flags & RB_SINGLE;
	m_uconf = flags & RB_CONFIG;
	m_verbose = flags & RB_VERBOSE;
	m_kernel = GetDocument()->m_kernelname;
}

void CBootDialog::OnBoot()
{
	// TODO: Add extra validation here
	WORD flags = 0;

	UpdateData(TRUE);

	flags |= m_askfor  ? RB_ASKNAME  : 0;
	flags |= m_cdrom   ? RB_CDROM    : 0;
	flags |= m_kdb     ? RB_KDB      : 0;
	flags |= m_rootdev ? RB_DFLTROOT : 0;
	flags |= m_singleu ? RB_SINGLE   : 0;
	flags |= m_uconf   ? RB_CONFIG   : 0;
	flags |= m_verbose ? RB_VERBOSE  : 0;

	if (m_dos) flags |= RB_DFLTROOT;

    GetDocument()->m_bootflags = flags;
    GetDocument()->m_kernelname = m_kernel;
    GetDocument()->m_bootdos = m_dos;

	GetDocument()->BootFreeBSD();
}

void CBootDialog::OnCancel()
{
	// TODO: Add extra cleanup here
	CWinApp *app=AfxGetApp();
	CWnd *wnd=app->m_pMainWnd;

	wnd->DestroyWindow();
}

extern "C" {
#include "..\fbsdboot\boot.h"
#include <dos.h>

#define SEG(a)		((unsigned int)(((long)(a))>>16l))
#define OFF(a)		((unsigned int)((long)(a)))

typedef struct _stackframe_ {
	DWORD edi, esi, ebp, dummy, ebx, edx, ecx, eax;
	WORD flags, es, ds, fs, gs, ip, cs, sp, ss;
} DPMI_STACK;


extern int openrd(char *);
extern unsigned _bios_disk(unsigned, struct _diskinfo_t *);

int biosread(int dev, int track, int head, int sector, int cnt, unsigned char far *buffer)
{
	unsigned segment, selector, stacksel;
	DPMI_STACK far *stack=NULL;
	DWORD rm_ptr, bios_ptr;
	char far *buf;

	bios_ptr = GlobalDosAlloc(sizeof(DPMI_STACK));
	if (!bios_ptr) {
		AfxMessageBox("Can't allocate DOS memory!", MB_OK, 0);
		return -1;
	}

	stack = (DPMI_STACK far *) _MK_FP(OFF(bios_ptr), 0);
	_fmemset(stack, 0, sizeof(DPMI_STACK));
	stacksel = OFF(bios_ptr);

	rm_ptr = GlobalDosAlloc(cnt*512l);	/* alloc enough memory */
	if (!rm_ptr) {
		GlobalDosFree(stacksel);
		AfxMessageBox("Can't allocate DOS memory!", MB_OK, 0);
		return -1;
	}

	segment = SEG(rm_ptr);
	selector = OFF(rm_ptr);

	stack->ss = stack->sp = 0;			/* DPMI provides stack */
	stack->cs = stack->ip = 0;			/* DPMI ignores CS:IP */

	stack->edx = dev&0xff;				/* hard disk */
    stack->edx |= (head<<8);			/* head # */
	stack->ecx = (sector+1)&0x3f;		/* sector # */
	stack->ecx |= (track<<8);
	stack->ecx |= ((track&0x300)>>2);	/* track # */
	stack->eax = cnt&0xff;				/* only "cnt" sectors */
	stack->eax |= 0x0200;				/* READ SECTOR Command */
	stack->es = segment;				/* Segment of buffer */
	stack->ebx = 0;						/* Offset of buffer */

	_asm {
		mov ax,0300h			; Simulate Real Mode Interrupt
		mov bx,0013h			; call int 13h
		mov cx,0				; nothing to copy
		mov di,0
		mov dx,stacksel
		mov es,dx				; ES:DI points to DPMI_STACK
		int 31h					; call DPMI services
		jnc done
	}

	AfxMessageBox("Error using DPMI services!", MB_OK, 0);
	GlobalDosFree(stacksel);
	GlobalDosFree(selector);
	return -1;

done:
	buf = (char far *) _MK_FP(selector, 0);
	_fmemcpy(buffer, buf, cnt*512);
	GlobalDosFree(stacksel);
	GlobalDosFree(selector);
	return 0;
}

unsigned long get_diskinfo(int drive)
{
	DPMI_STACK far *stack=NULL;
	DWORD bios_ptr, rt;
	unsigned stacksel;

	bios_ptr = GlobalDosAlloc(sizeof(DPMI_STACK));
	if (!bios_ptr) {
		AfxMessageBox("Can't allocate DOS memory!", MB_OK, 0);
		return -1;
	}

	stack = (DPMI_STACK far *) _MK_FP(OFF(bios_ptr), 0);
	_fmemset(stack, 0, sizeof(DPMI_STACK));
	stacksel = OFF(bios_ptr);

	stack->ss = stack->sp = 0;			/* DPMI provides stack */
	stack->cs = stack->ip = 0;			/* DPMI ignores CS:IP */

	stack->edx = drive&0xff;			/* hard disk */
	stack->eax = 0x0800;				/* GetDiskInfo Command */

	_asm {
		mov ax,0300h			; Simulate Real Mode Interrupt
		mov bx,0013h			; call int 13h
		mov cx,0				; nothing to copy
		mov di,0
		mov dx,stacksel
		mov es,dx				; ES:DI points to DPMI_STACK
		int 31h					; call DPMI services
		jnc done
	}

	AfxMessageBox("Error using DPMI services!", MB_OK, 0);
	GlobalDosFree(stacksel);
	return -1;

done:
	if ((stack->eax&0xffff)) {
		/*
		 * Failure! We assume it's a floppy!
		 */
		stack->eax = 0;
		stack->ebx = 2;
		stack->ecx = 79*256+15;
		stack->edx = 0x0101;
	}

	rt = (((stack->ecx&0xc0)<<2l) | ((stack->ecx&0xff00)>>8)) << 16l |
	 	 ((stack->edx&0xff00) | (stack->ecx&0x3f));

	GlobalDosFree(stacksel);
	return rt;
}

static struct _kname_ {
	char *name;
	struct _kname_ *next;
} *kfirst=NULL, *klast=NULL;

void AddKernel(char *name)
{
	if (strcmp(name, ".") == 0) return;
	if (strcmp(name, "..") == 0) return;
	if (strcmp(name, "bin") == 0) return;
	if (strcmp(name, "etc") == 0) return;
	if (strcmp(name, "sbin") == 0) return;
	if (strcmp(name, "stand") == 0) return;
	if (strcmp(name, "usr") == 0) return;
	if (strcmp(name, "lkm") == 0) return;
	if (strcmp(name, "sys") == 0) return;
	if (strcmp(name, "root") == 0) return;
	if (strcmp(name, "dev") == 0) return;
	if (strcmp(name, "proc") == 0) return;
	if (strcmp(name, "mnt") == 0) return;
	if (strcmp(name, "tmp") == 0) return;
	if (strcmp(name, "var") == 0) return;
	if (strcmp(name, "lost+found") == 0) return;

	struct _kname_ *n=(struct _kname_ *) malloc(sizeof(struct _kname_));

	if (!n) return;
	n->next = NULL;
	n->name = strdup(name);

	if (kfirst == NULL) {
		kfirst = klast = n;
	} else {
		klast->next = n;
		klast = n;
	}
}

void CleanUp(void)
{
	struct _kname_ *n=kfirst, *o;

	while (o=n) {
		free(o->name);
		n = o->next;
		free(o);
	}
	kfirst = klast = NULL;
}

};

void CBootDialog::OnSearch()
{
	// TODO: Add your control notification handler code here
	if (m_dos) {
		CFileDialog dialog(TRUE, NULL, NULL, OFN_HIDEREADONLY, "All Files (*.*) | *.* ||");
		if (dialog.DoModal() == IDCANCEL) return;
		m_kernel = dialog.GetPathName();
	} else {
		CBSDSelect dlg;
		int drive=0x80;

		part = unit = 0;
		maj = (drive&0x80 ? 0 : 2);		/* a good first bet */

		CleanUp();

		if (openrd("?")) {				/* error (!!) getting infos */
			UpdateData(FALSE);
			return;
		}

		struct _kname_ *n=kfirst;
		while (n) {
			dlg.AddName(n->name);
			n = n->next;
		}

		if (dlg.DoModal() == IDOK) {
			m_kernel = "/" + dlg.m_sel;
		}

	}
	UpdateData(FALSE);
}

void CBootDialog::OnDos()
{
	// TODO: Add your control notification handler code here
	m_dos = !m_dos;
}
