Rev 555 | Blame | Last modification | View Log | RSS feed
/*
* SVARDOS INSTALL PROGRAM
* Copyright (C) 2021 Mateusz Viste
*
* PUBLISHED UNDER THE TERMS OF THE MIT LICENSE
*
* COPYRIGHT (C) 2016-2021 MATEUSZ VISTE, ALL RIGHTS RESERVED.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* http://svardos.osdn.io
*/
#include <dos.h>
#include <direct.h> /* mkdir() */
#include <stdio.h> /* printf() and friends */
#include <stdlib.h> /* system() */
#include <string.h> /* memcpy() */
#include <unistd.h>
#include "kitten\kitten.h"
#include "input.h"
#include "video.h"
/* keyboard layouts and locales */
#include "keylay.h"
#include "keyoff.h"
/* color scheme (color, mono) */
static unsigned short COLOR_TITLEBAR[2] = {0x7000,0x7000};
static unsigned short COLOR_BODY[2] = {0x1700,0x0700};
static unsigned short COLOR_SELECT[2] = {0x7000,0x7000};
static unsigned short COLOR_SELECTCUR[2] = {0x1F00,0x0700};
/* mono flag */
static int mono = 0;
/* how much disk space does SvarDOS require (in MiB) */
#define SVARDOS_DISK_REQ 8
/* menu screens can output only one of these: */
#define MENUNEXT 0
#define MENUPREV -1
#define MENUQUIT -2
/* a convenience 'function' used for debugging */
#define DBG(x) { video_putstringfix(24, 0, 0x4F00u, x, 80); }
struct slocales {
char lang[4];
char *keybcode;
unsigned int codepage;
int egafile;
int keybfile;
int keyboff;
int keyblen;
unsigned int keybid;
};
/* reboot the computer */
static void reboot(void) {
void ((far *bootroutine)()) = (void (far *)()) 0xFFFF0000L;
int far *rstaddr = (int far *)0x00400072L; /* BIOS boot flag is at 0040:0072 */
*rstaddr = 0x1234; /* 0x1234 = warm boot, 0 = cold boot */
(*bootroutine)(); /* jump to the BIOS reboot routine at FFFF:0000 */
}
/* outputs a string to screen with taking care of word wrapping. returns amount of lines. */
static int putstringwrap(int y, int x, unsigned short attr, char *s) {
int linew, lincount;
linew = 80;
if (x >= 0) linew -= (x << 1);
for (lincount = 1; y+lincount < 25; lincount++) {
int i, len = linew;
for (i = 0; i <= linew; i++) {
if (s[i] == ' ') len = i;
if (s[i] == '\n') {
len = i;
break;
}
if (s[i] == 0) {
len = i;
break;
}
}
video_putstring(y++, x, attr, s, len);
s += len;
if (*s == 0) break;
s += 1; /* skip the whitespace char */
}
return(lincount);
}
/* an NLS wrapper around video_putstring(), also performs line wrapping when
* needed. returns the amount of lines that were output */
static int putstringnls(int y, int x, unsigned short attr, int nlsmaj, int nlsmin, char *s) {
s = kittengets(nlsmaj, nlsmin, s);
return(putstringwrap(y, x, attr, s));
}
/* copy file f1 to f2 using buff as a buffer of buffsz bytes. f2 will be overwritten if it
* exists already! returns 0 on success. */
static int fcopy(const char *f2, const char *f1, void *buff, size_t buffsz) {
FILE *fd1, *fd2;
size_t sz;
int res = -1; /* assume failure */
/* open files */
fd1 = fopen(f1, "rb");
fd2 = fopen(f2, "wb");
if ((fd1 == NULL) || (fd2 == NULL)) goto QUIT;
/* copy data */
for (;;) {
sz = fread(buff, 1, buffsz, fd1);
if (sz == 0) {
if (feof(fd1) != 0) break;
goto QUIT;
}
if (fwrite(buff, 1, sz, fd2) != sz) goto QUIT;
}
res = 0; /* success */
QUIT:
if (fd1 != NULL) fclose(fd1);
if (fd2 != NULL) fclose(fd2);
return(res);
}
static int menuselect(int ypos, int xpos, int height, char **list, int listlen) {
int i, offset = 0, res = 0, count, width = 0;
/* count how many positions there is, and check their width */
for (count = 0; (list[count] != NULL) && (count != listlen); count++) {
int len = strlen(list[count]);
if (len > width) width = len;
}
/* if xpos negative, means 'center out' */
if (xpos < 0) xpos = 39 - (width >> 1);
video_putchar(ypos, xpos+width+2, COLOR_SELECT[mono], 0xBF); /* \ */
video_putchar(ypos, xpos-1, COLOR_SELECT[mono], 0xDA); /* / */
video_putchar(ypos+height-1, xpos-1, COLOR_SELECT[mono], 0xC0); /* \ */
video_putchar(ypos+height-1, xpos+width+2, COLOR_SELECT[mono], 0xD9);/* / */
video_putcharmulti(ypos, xpos, COLOR_SELECT[mono], 0xC4, width + 2, 1);
video_putcharmulti(ypos+height-1, xpos, COLOR_SELECT[mono], 0xC4, width + 2, 1);
video_putcharmulti(ypos+1, xpos-1, COLOR_SELECT[mono], 0xB3, height - 2, 80);
video_putcharmulti(ypos+1, xpos+width+2, COLOR_SELECT[mono], 0xB3, height - 2, 80);
for (;;) {
int key;
/* list of selectable items */
for (i = 0; i < height - 2; i++) {
if (i + offset == res) {
video_putchar(ypos + 1 + i, xpos, COLOR_SELECTCUR[mono], 16);
video_putchar(ypos + 1 + i, xpos+width+1, COLOR_SELECTCUR[mono], 17);
video_movecursor(ypos + 1 + i, xpos);
video_putstringfix(ypos + 1 + i, xpos+1, COLOR_SELECTCUR[mono], list[i + offset], width);
} else if (i + offset < count) {
video_putchar(ypos + 1 + i, xpos, COLOR_SELECT[mono], ' ');
video_putchar(ypos + 1 + i, xpos+width+1, COLOR_SELECT[mono], ' ');
video_putstringfix(ypos + 1 + i, xpos+1, COLOR_SELECT[mono], list[i + offset], width);
} else {
video_putcharmulti(ypos + 1 + i, xpos, COLOR_SELECT[mono], ' ', width+2, 1);
}
}
key = input_getkey();
if (key == 0x0D) { /* ENTER */
return(res);
} else if (key == 0x148) { /* up */
if (res > 0) {
res--;
if (res < offset) offset = res;
}
} else if (key == 0x150) { /* down */
if (res+1 < count) {
res++;
if (res > offset + height - 3) offset = res - (height - 3);
}
} else if (key == 0x147) { /* home */
res = 0;
offset = 0;
} else if (key == 0x14F) { /* end */
res = count - 1;
if (res > offset + height - 3) offset = res - (height - 3);
} else if (key == 0x1B) { /* ESC */
return(-1);
}/* else {
char buf[8];
snprintf(buf, sizeof(buf), "0x%02X ", key);
video_putstring(1, 0, COLOR_BODY[mono], buf, -1);
}*/
}
}
static void newscreen(int statusbartype) {
char *msg;
msg = kittengets(0, 0, "SVARDOS INSTALLATION");
video_putcharmulti(0, 0, COLOR_TITLEBAR[mono], ' ', 80, 1);
video_putstring(0, 40 - (strlen(msg) >> 1), COLOR_TITLEBAR[mono], msg, -1);
video_clear(COLOR_BODY[mono], 80, -80);
switch (statusbartype) {
case 1:
msg = kittengets(0, 11, "Up/Down = Select entry | Enter = Validate your choice | ESC = Quit to DOS");
break;
case 2:
msg = kittengets(0, 5, "Press any key...");
break;
case 3:
msg = "";
break;
default:
msg = kittengets(0, 10, "Up/Down = Select entry | Enter = Validate your choice | ESC = Previous screen");
break;
}
video_putchar(24, 0, COLOR_TITLEBAR[mono], ' ');
video_putstringfix(24, 1, COLOR_TITLEBAR[mono], msg, 79);
video_movecursor(25,0);
}
/* fills a slocales struct accordingly to the value of its keyboff member */
static void kblay2slocal(struct slocales *locales) {
char *m;
for (m = kblayouts[locales->keyboff]; *m != 0; m++); /* skip layout name */
m++;
/* skip keyb code and copy it to locales.keybcode */
locales->keybcode = m;
for (; *m != 0; m++);
/* */
locales->codepage = ((unsigned short)m[1] << 8) | m[2];
locales->egafile = m[3];
locales->keybfile = m[4];
locales->keybid = ((unsigned short)m[5] << 8) | m[6];
}
static int selectlang(struct slocales *locales) {
int choice, x;
char *msg;
char *langlist[] = {
"English",
"French",
"German",
"Italian",
"Polish",
"Russian",
"Slovene",
"Swedish",
"Turkish",
NULL
};
newscreen(1);
msg = kittengets(1, 0, "Welcome to SvarDOS");
x = 40 - (strlen(msg) >> 1);
video_putstring(4, x, COLOR_BODY[mono], msg, -1);
video_putcharmulti(5, x, COLOR_BODY[mono], '=', strlen(msg), 1);
putstringnls(8, -1, COLOR_BODY[mono], 1, 1, "Please select your language from the list below:");
choice = menuselect(11, -1, 11, langlist, -1);
if (choice < 0) return(MENUPREV);
/* populate locales with default values */
memset(locales, 0, sizeof(struct slocales));
switch (choice) {
case 1:
strcpy(locales->lang, "FR");
locales->keyboff = OFFLOC_FR;
locales->keyblen = OFFLEN_FR;
break;
case 2:
strcpy(locales->lang, "DE");
locales->keyboff = OFFLOC_DE;
locales->keyblen = OFFLEN_DE;
break;
case 3:
strcpy(locales->lang, "IT");
locales->keyboff = OFFLOC_IT;
locales->keyblen = OFFLEN_IT;
break;
case 4:
strcpy(locales->lang, "PL");
locales->keyboff = OFFLOC_PL;
locales->keyblen = OFFLEN_PL;
break;
case 5:
strcpy(locales->lang, "RU");
locales->keyboff = OFFLOC_RU;
locales->keyblen = OFFLEN_RU;
break;
case 6:
strcpy(locales->lang, "SI");
locales->keyboff = OFFLOC_SI;
locales->keyblen = OFFLEN_SI;
break;
case 7:
strcpy(locales->lang, "SV");
locales->keyboff = OFFLOC_SV;
locales->keyblen = OFFLEN_SV;
break;
case 8:
strcpy(locales->lang, "TR");
locales->keyboff = OFFLOC_TR;
locales->keyblen = OFFLEN_TR;
break;
default:
strcpy(locales->lang, "EN");
locales->keyboff = 0;
locales->keyblen = OFFCOUNT;
break;
}
/* populate the slocales struct accordingly to the keyboff member */
kblay2slocal(locales);
/* */
return(MENUNEXT);
}
static int selectkeyb(struct slocales *locales) {
int menuheight, choice;
if (locales->keyblen == 1) return(MENUNEXT); /* do not ask for keyboard layout if only one is available for given language */
newscreen(0);
putstringnls(5, 1, COLOR_BODY[mono], 1, 5, "SvarDOS supports different keyboard layouts. Choose the keyboard layout that you want.");
menuheight = locales->keyblen + 2;
if (menuheight > 13) menuheight = 13;
choice = menuselect(10, -1, menuheight, &(kblayouts[locales->keyboff]), locales->keyblen);
if (choice < 0) return(MENUPREV);
/* (re)load the keyboard layout & codepage setup */
locales->keyboff += choice;
kblay2slocal(locales);
return(MENUNEXT);
}
/* returns 0 if installation must proceed, non-zero otherwise */
static int welcomescreen(void) {
int c;
char *choice[] = {"Install SvarDOS to disk", "Quit to DOS", NULL};
choice[0] = kittengets(0, 1, choice[0]);
choice[1] = kittengets(0, 2, choice[1]);
newscreen(0);
putstringnls(4, 1, COLOR_BODY[mono], 2, 0, "You are about to install SvarDOS: a free, MSDOS-compatible operating system based on the FreeDOS kernel. SvarDOS targets 386+ computers and comes with a variety of third-party applications.\n\nWARNING: If your PC has another operating system installed, this other system might be unable to boot once SvarDOS is installed.");
c = menuselect(13, -1, 4, choice, -1);
if (c < 0) return(MENUPREV);
if (c == 0) return(MENUNEXT);
return(MENUQUIT);
}
/* returns 1 if drive is removable, 0 if not, -1 on error */
static int isdriveremovable(int drv) {
union REGS r;
r.x.ax = 0x4408;
r.h.bl = drv;
int86(0x21, &r, &r);
/* CF set on error, AX set to 0 if removable, 1 if fixed */
if (r.x.cflag != 0) return(-1);
if (r.x.ax == 0) return(1);
return(0);
}
/* returns total disk space of drive drv (in MiB, max 2048), or -1 if drive invalid */
static int disksize(int drv) {
long res;
union REGS r;
r.h.ah = 0x36; /* DOS 2+ get free disk space */
r.h.dl = drv; /* A=1, B=2, etc */
int86(0x21, &r, &r);
if (r.x.ax == 0xffffu) return(-1); /* AX set to FFFFh if drive invalid */
res = r.x.ax; /* sectors per cluster */
res *= r.x.dx; /* dx contains total clusters, bx contains free clusters */
res *= r.x.cx; /* bytes per sector */
res >>= 20; /* convert bytes to MiB */
return(res);
}
/* returns 0 if disk is empty, non-zero otherwise */
static int diskempty(int drv) {
unsigned int rc;
int res;
char buff[8];
struct find_t fileinfo;
snprintf(buff, sizeof(buff), "%c:\\*.*", 'A' + drv - 1);
rc = _dos_findfirst(buff, _A_NORMAL | _A_SUBDIR | _A_HIDDEN | _A_SYSTEM, &fileinfo);
if (rc == 0) {
res = 1; /* call successfull means disk is not empty */
} else {
res = 0;
}
/* _dos_findclose(&fileinfo); */ /* apparently required only on OS/2 */
return(res);
}
#ifdef DEADCODE
/* set new DOS "current drive" to drv ('A', 'B', etc). returns 0 on success */
static int set_cur_drive(char drv) {
union REGS r;
if ((drv < 'A') || (drv > 'Z')) return(-1);
r.h.ah = 0x0E; /* DOS 1+ SELECT DEFAULT DRIVE */
r.h.dl = drv - 'A';
int86(0x21, &r, &r);
if (r.h.al < drv - 'A') return(-1);
return(0);
}
#endif
/* get the DOS "current drive" (0=A:, 1=B:, etc) */
static int get_cur_drive(void) {
union REGS r;
r.h.ah = 0x19; /* DOS 1+ GET CURRENT DEFAULT DRIVE */
int86(0x21, &r, &r);
return(r.h.al);
}
/* returns 0 if file exists, non-zero otherwise */
static int fileexists(const char *fname) {
FILE *fd;
fd = fopen(fname, "rb");
if (fd == NULL) return(-1);
fclose(fd);
return(0);
}
static int preparedrive(char sourcedrv) {
int driveremovable;
int selecteddrive = 3; /* default to 'C:' */
int cselecteddrive;
int ds;
int choice;
char buff[1024];
int driveid = 1; /* fdisk runs on first drive (unless USB boot) */
if (selecteddrive == get_cur_drive() + 1) { /* get_cur_drive() returns 0-based values (A=0) while selecteddrive is 1-based (A=1) */
selecteddrive = 4; /* use D: if install is run from C: (typically because it was booted from USB?) */
driveid = 2; /* primary drive is the emulated USB storage */
}
cselecteddrive = 'A' + selecteddrive - 1;
for (;;) {
driveremovable = isdriveremovable(selecteddrive);
if (driveremovable < 0) {
char *list[] = { "Create a partition automatically", "Run the FDISK partitioning tool", "Quit to DOS", NULL};
newscreen(0);
list[0] = kittengets(0, 3, list[0]);
list[1] = kittengets(0, 4, list[1]);
list[2] = kittengets(0, 2, list[2]);
snprintf(buff, sizeof(buff), kittengets(3, 0, "ERROR: Drive %c: could not be found. Perhaps your hard disk needs to be partitioned first. Please create at least one partition on your hard disk, so SvarDOS can be installed on it. Note, that SvarDOS requires at least %d MiB of available disk space.\n\nYou can use the FDISK partitioning tool for creating the required partition manually, or you can let the installer partitioning your disk automatically. You can also abort the installation to use any other partition manager of your choice."), cselecteddrive, SVARDOS_DISK_REQ);
switch (menuselect(6 + putstringwrap(4, 1, COLOR_BODY[mono], buff), -1, 5, list, -1)) {
case 0:
sprintf(buff, "FDISK /AUTO %d", driveid);
system(buff);
break;
case 1:
video_clear(0x0700, 0, 0);
video_movecursor(0, 0);
sprintf(buff, "FDISK %d", driveid);
system(buff);
break;
case 2:
return(MENUQUIT);
default:
return(-1);
}
/* write a temporary MBR which only skips the drive (in case BIOS would
* try to boot off the not-yet-ready C: disk) */
sprintf(buff, "FDISK /AMBR %d", driveid);
system(buff); /* writes BOOT.MBR into actual MBR */
newscreen(2);
putstringnls(10, 10, COLOR_BODY[mono], 3, 1, "Your computer will reboot now.");
putstringnls(12, 10, COLOR_BODY[mono], 0, 5, "Press any key...");
input_getkey();
reboot();
return(MENUQUIT);
} else if (driveremovable > 0) {
newscreen(2);
snprintf(buff, sizeof(buff), kittengets(3, 2, "ERROR: Drive %c: is a removable device. Installation aborted."), cselecteddrive);
video_putstring(9, 1, COLOR_BODY[mono], buff, -1);
putstringnls(11, 2, COLOR_BODY[mono], 0, 5, "Press any key...");
return(MENUQUIT);
}
/* if not formatted, propose to format it right away (try to create a directory) */
snprintf(buff, sizeof(buff), "%c:\\SVWRTEST.123", cselecteddrive);
if (mkdir(buff) == 0) {
rmdir(buff);
} else {
char *list[3];
newscreen(0);
snprintf(buff, sizeof(buff), kittengets(3, 3, "ERROR: Drive %c: seems to be unformated. Do you wish to format it?"), cselecteddrive);
video_putstring(7, 1, COLOR_BODY[mono], buff, -1);
snprintf(buff, sizeof(buff), kittengets(0, 7, "Format drive %c:"), cselecteddrive);
list[0] = buff;
list[1] = kittengets(0, 2, "Quit to DOS");
list[2] = NULL;
choice = menuselect(12, -1, 4, list, -1);
if (choice < 0) return(MENUPREV);
if (choice == 1) return(MENUQUIT);
video_clear(0x0700, 0, 0);
video_movecursor(0, 0);
snprintf(buff, sizeof(buff), "FORMAT %c: /Q /U /Z:seriously /V:SVARDOS", cselecteddrive);
system(buff);
continue;
}
/* check total disk space */
ds = disksize(selecteddrive);
if (ds < SVARDOS_DISK_REQ) {
int y = 9;
newscreen(2);
snprintf(buff, sizeof(buff), kittengets(3, 4, "ERROR: Drive %c: is not big enough! SvarDOS requires a disk of at least %d MiB."), cselecteddrive, SVARDOS_DISK_REQ);
y += putstringwrap(y, 1, COLOR_BODY[mono], buff);
putstringnls(++y, 1, COLOR_BODY[mono], 0, 5, "Press any key...");
input_getkey();
return(MENUQUIT);
}
/* is the disk empty? */
newscreen(0);
if (diskempty(selecteddrive) != 0) {
char *list[3];
int y = 6;
snprintf(buff, sizeof(buff), kittengets(3, 5, "ERROR: Drive %c: is not empty. SvarDOS must be installed on an empty disk.\n\nYou can format the disk now, to make it empty. Note however, that this will ERASE ALL CURRENT DATA on your disk."), cselecteddrive);
y += putstringwrap(y, 1, COLOR_BODY[mono], buff);
snprintf(buff, sizeof(buff), kittengets(0, 7, "Format drive %c:"), cselecteddrive);
list[0] = buff;
list[1] = kittengets(0, 2, "Quit to DOS");
list[2] = NULL;
choice = menuselect(++y, -1, 4, list, -1);
if (choice < 0) return(MENUPREV);
if (choice == 1) return(MENUQUIT);
video_clear(0x0700, 0, 0);
video_movecursor(0, 0);
snprintf(buff, sizeof(buff), "FORMAT %c: /Q /U /Z:seriously /V:SVARDOS", cselecteddrive);
system(buff);
continue;
} else {
/* final confirmation */
char *list[] = { "Install SvarDOS", "Quit to DOS", NULL};
list[0] = kittengets(0, 1, list[0]);
list[1] = kittengets(0, 2, list[1]);
snprintf(buff, sizeof(buff), kittengets(3, 6, "The installation of SvarDOS to %c: is about to begin."), cselecteddrive);
video_putstring(7, -1, COLOR_BODY[mono], buff, -1);
choice = menuselect(10, -1, 4, list, -1);
if (choice < 0) return(MENUPREV);
if (choice == 1) return(MENUQUIT);
snprintf(buff, sizeof(buff), "SYS %c: %c: > NUL", sourcedrv, cselecteddrive);
system(buff);
sprintf(buff, "FDISK /MBR %d", driveid);
system(buff);
snprintf(buff, sizeof(buff), "%c:\\TEMP", cselecteddrive);
mkdir(buff);
return(cselecteddrive);
}
}
}
/* generates locales-related configurations and writes them to file (this
* is used to compute autoexec.bat content) */
static void genlocalesconf(FILE *fd, const struct slocales *locales) {
if (locales == NULL) return;
fprintf(fd, "SET LANG=%s\r\n", locales->lang);
if (locales->egafile > 0) {
fprintf(fd, "DISPLAY CON=(EGA,,1)\r\n");
if (locales->egafile == 1) {
fprintf(fd, "MODE CON CP PREPARE=((%u) %%DOSDIR%%\\CPI\\EGA.CPX)\r\n", locales->codepage);
} else {
fprintf(fd, "MODE CON CP PREPARE=((%u) %%DOSDIR%%\\CPI\\EGA%d.CPX)\r\n", locales->codepage, locales->egafile);
}
fprintf(fd, "MODE CON CP SELECT=%u\r\n", locales->codepage);
}
if (locales->keybfile > 0) {
fprintf(fd, "KEYB %s,%d,%%DOSDIR%%\\BIN\\", locales->keybcode, locales->codepage);
if (locales->keybfile == 1) {
fprintf(fd, "KEYBOARD.SYS");
} else {
fprintf(fd, "KEYBRD%d.SYS", locales->keybfile);
}
if (locales->keybid != 0) fprintf(fd, " /ID:%d", locales->keybid);
fprintf(fd, "\r\n");
}
}
static void bootfilesgen(char targetdrv, const struct slocales *locales) {
char buff[128];
FILE *fd;
/*** CONFIG.SYS ***/
snprintf(buff, sizeof(buff), "%c:\\TEMP\\CONFIG.SYS", targetdrv);
fd = fopen(buff, "wb");
if (fd == NULL) return;
fprintf(fd, "DOS=UMB,HIGH\r\n"
"LASTDRIVE=Z\r\n"
"FILES=50\r\n");
fprintf(fd, "DEVICE=C:\\SVARDOS\\BIN\\HIMEMX.EXE\r\n");
if (strcmp(locales->lang, "EN") == 0) {
strcpy(buff, "COMMAND");
} else {
snprintf(buff, sizeof(buff), "CMD-%s", locales->lang);
}
fprintf(fd, "SHELLHIGH=C:\\SVARDOS\\BIN\\%s.COM /E:512 /P\r\n", buff);
fprintf(fd, "REM COUNTRY=001,437,C:\\SVARDOS\\CONF\\COUNTRY.SYS\r\n");
fprintf(fd, "REM DEVICE=C:\\DRIVERS\\UDVD2\\UDVD2.SYS /D:SVCD0001 /H\r\n");
fclose(fd);
/*** AUTOEXEC.BAT ***/
snprintf(buff, sizeof(buff), "%c:\\TEMP\\AUTOEXEC.BAT", targetdrv);
fd = fopen(buff, "wb");
if (fd == NULL) return;
fprintf(fd, "@ECHO OFF\r\n");
fprintf(fd, "SET TEMP=C:\\TEMP\r\n");
fprintf(fd, "SET DOSDIR=C:\\SVARDOS\r\n");
fprintf(fd, "SET NLSPATH=%%DOSDIR%%\\NLS\r\n");
fprintf(fd, "SET DIRCMD=/OGNE/P/4\r\n");
fprintf(fd, "SET WATTCP.CFG=%%DOSDIR%%\\CFG\r\n");
fprintf(fd, "PATH %%DOSDIR%%\\BIN\r\n");
fprintf(fd, "PROMPT $P$G\r\n");
fprintf(fd, "ALIAS REBOOT=FDAPM COLDBOOT\r\n");
fprintf(fd, "ALIAS HALT=FDAPM POWEROFF\r\n");
fprintf(fd, "FDAPM APMDOS\r\n");
fprintf(fd, "\r\n");
genlocalesconf(fd, locales);
fprintf(fd, "\r\n");
fprintf(fd, "REM Uncomment the line below for CDROM support\r\n");
fprintf(fd, "REM SHSUCDX /d:SVCD0001\r\n");
fprintf(fd, "\r\n");
fprintf(fd, "REM Uncomment the line below for automatic mouse support\r\n");
fprintf(fd, "REM CTMOUSE\r\n");
fprintf(fd, "\r\n");
fprintf(fd, "ECHO.\r\n");
fprintf(fd, "ECHO %s\r\n", kittengets(6, 0, "Welcome to SvarDOS! Type 'HELP' if you need help."));
fclose(fd);
/*** CREATE DIRECTORY FOR CONFIGURATION FILES ***/
snprintf(buff, sizeof(buff), "%c:\\SVARDOS", targetdrv);
mkdir(buff);
snprintf(buff, sizeof(buff), "%c:\\SVARDOS\\CFG", targetdrv);
mkdir(buff);
/*** PKG.CFG ***/
snprintf(buff, sizeof(buff), "%c:\\SVARDOS\\CFG\\PKG.CFG", targetdrv);
fd = fopen(buff, "wb");
if (fd == NULL) return;
fprintf(fd, "# pkg config file - specifies locations where packages should be installed\r\n"
"\r\n"
"# Programs\r\n"
"DIR PROGS C:\\\r\n"
"\r\n"
"# Games \r\n"
"DIR GAMES C:\\\r\n"
"\r\n"
"# Drivers\r\n"
"DIR DRIVERS C:\\DRIVERS\r\n"
"\r\n"
"# Development tools\r\n"
"DIR DEVEL C:\\DEVEL\r\n");
fclose(fd);
/*** COUNTRY.SYS ***/
/*** PICOTCP ***/
/*** WATTCP ***/
snprintf(buff, sizeof(buff), "%c:\\SVARDOS\\CFG\\WATTCP.CFG", targetdrv);
fd = fopen(buff, "wb");
if (fd == NULL) return;
fprintf(fd, "my_ip = dhcp\r\n"
"#my_ip = 192.168.0.7\r\n"
"#netmask = 255.255.255.0\r\n"
"#nameserver = 192.168.0.1\r\n"
"#nameserver = 192.168.0.2\r\n"
"#gateway = 192.168.0.1\r\n");
fclose(fd);
}
static int installpackages(char targetdrv, char srcdrv, const struct slocales *locales) {
char pkglist[512];
int i, pkglistlen;
size_t pkglistflen;
char buff[1024]; /* must be *at least* 1 sector big for efficient file copying */
FILE *fd = NULL;
char *pkgptr;
newscreen(3);
/* load pkg list */
fd = fopen("install.lst", "rb");
if (fd == NULL) {
video_putstring(10, 30, COLOR_BODY[mono], "ERROR: INSTALL.LST NOT FOUND", -1);
input_getkey();
return(-1);
}
pkglistflen = fread(pkglist, 1, sizeof(pkglist), fd);
fclose(fd);
if (pkglistflen == sizeof(pkglist)) {
video_putstring(10, 30, COLOR_BODY[mono], "ERROR: INSTALL.LST TOO LARGE", -1);
input_getkey();
return(-1);
}
pkglist[pkglistflen] = 0xff; /* mark the end of list */
/* replace all \r and \n chars by 0 bytes, and count the number of packages */
pkglistlen = 0;
for (i = 0; i < pkglistflen; i++) {
switch (pkglist[i]) {
case '\n':
pkglistlen++;
/* FALLTHRU */
case '\r':
pkglist[i] = 0;
break;
}
}
/* copy pkg.exe to the new drive, along with all packages */
snprintf(buff, sizeof(buff), "%c:\\TEMP\\pkg.exe", targetdrv);
snprintf(buff + 64, sizeof(buff) - 64, "%c:\\pkg.exe", srcdrv);
fcopy(buff, buff + 64, buff, sizeof(buff));
/* open the post-install autoexec.bat and prepare initial instructions */
snprintf(buff, sizeof(buff), "%c:\\temp\\postinst.bat", targetdrv);
fd = fopen(buff, "wb");
if (fd == NULL) return(-1);
fprintf(fd, "@ECHO OFF\r\n");
/* copy packages */
pkgptr = pkglist;
for (i = 0;; i++) {
/* move forward to nearest entry or end of list */
while (*pkgptr == 0) pkgptr++;
if (*pkgptr == 0xff) break;
/* install the package */
snprintf(buff, sizeof(buff), kittengets(4, 0, "Installing package %d/%d: %s"), i+1, pkglistlen, pkgptr);
strcat(buff, " ");
video_putstringfix(10, 1, COLOR_BODY[mono], buff, sizeof(buff));
/* wait for new diskette if package not found */
snprintf(buff, sizeof(buff), "%c:\\%s.zip", srcdrv, pkgptr);
while (fileexists(buff) != 0) {
putstringnls(12, 1, COLOR_BODY[mono], 4, 1, "*** INSERT THE DISK THAT CONTAINS THE REQUIRED FILE AND PRESS ANY KEY ***");
input_getkey();
video_putstringfix(12, 1, COLOR_BODY[mono], "", 80); /* erase the 'insert disk' message */
}
/* proceed with package copy (buff contains the src filename already) */
snprintf(buff + 32, sizeof(buff) - 32, "%c:\\temp\\%s.zip", targetdrv, pkgptr);
if (fcopy(buff + 32, buff, buff, sizeof(buff)) != 0) {
video_putstring(10, 30, COLOR_BODY[mono], "READ ERROR", -1);
input_getkey();
fclose(fd);
return(-1);
}
/* write install instruction to post-install script */
fprintf(fd, "pkg install %s.zip\r\ndel %s.zip\r\n", pkgptr, pkgptr);
/* jump to next entry or end of list */
while ((*pkgptr != 0) && (*pkgptr != 0xff)) pkgptr++;
if (*pkgptr == 0xff) break;
}
/* set up locales so the "installation over" message is nicely displayed */
genlocalesconf(fd, locales);
/* replace autoexec.bat and config.sys now and write some nice message on screen */
fprintf(fd, "DEL pkg.exe\r\n"
"COPY CONFIG.SYS C:\\\r\n"
"DEL CONFIG.SYS\r\n"
"DEL C:\\AUTOEXEC.BAT\r\n"
"COPY AUTOEXEC.BAT C:\\\r\n"
"DEL AUTOEXEC.BAT\r\n");
/* print out the "installation over" message */
fprintf(fd, "ECHO.\r\n"
"ECHO %s\r\n"
"ECHO.\r\n", kittengets(5, 1, "SvarDOS installation is over. Please restart your computer now."));
fclose(fd);
/* prepare a dummy autoexec.bat that will call temp\postinst.bat */
snprintf(buff, sizeof(buff), "%c:\\autoexec.bat", targetdrv);
fd = fopen(buff, "wb");
if (fd == NULL) return(-1);
fprintf(fd, "@ECHO OFF\r\n"
"SET DOSDIR=C:\\SVARDOS\r\n"
"SET NLSPATH=%%DOSDIR%%\\NLS\r\n"
"PATH %%DOSDIR%%\\BIN\r\n");
fprintf(fd, "CD TEMP\r\n"
"postinst.bat\r\n");
fclose(fd);
return(0);
}
static void finalreboot(void) {
int y = 9;
newscreen(2);
y += putstringnls(y, 1, COLOR_BODY[mono], 5, 0, "Your computer will reboot now.\nPlease remove the installation disk from your drive.");
putstringnls(++y, 1, COLOR_BODY[mono], 0, 5, "Press any key...");
input_getkey();
reboot();
}
static void loadcp(const struct slocales *locales) {
char buff[64];
if (locales->codepage == 437) return;
video_movecursor(1, 0);
if (locales->egafile == 1) {
snprintf(buff, sizeof(buff), "MODE CON CP PREP=((%u) EGA.CPX) > NUL", locales->codepage);
} else {
snprintf(buff, sizeof(buff), "MODE CON CP PREP=((%u) EGA%d.CPX) > NUL", locales->codepage, locales->egafile);
}
system(buff);
snprintf(buff, sizeof(buff), "MODE CON CP SEL=%u > NUL", locales->codepage);
system(buff);
/* below I re-init the video controller - apparently this is required if
* I want the new glyph symbols to be actually applied, at least some
* (broken?) BIOSes, like VBox, apply glyphs only at next video mode change */
{
union REGS r;
r.h.ah = 0x0F; /* get current video mode */
int86(0x10, &r, &r); /* r.h.al contains the current video mode now */
r.h.al |= 128; /* set the high bit of AL to instruct BIOS not to flush VRAM's content (EGA+) */
r.h.ah = 0; /* re-set video mode (to whatever is set in AL) */
int86(0x10, &r, &r);
}
}
#ifdef DEADCODE
/* checks that drive drv contains SvarDOS packages
* returns 0 if found, non-zero otherwise */
static int checkinstsrc(char drv) {
char fname[16];
snprintf(fname, sizeof(fname), "%c:\\ATTRIB.ZIP", drv);
return(fileexists(fname));
}
#endif
int main(void) {
struct slocales locales;
int targetdrv;
int sourcedrv;
int action;
sourcedrv = get_cur_drive() + 'A';
/* init screen and detect mono status */
mono = video_init();
kittenopen("INSTALL"); /* load initial NLS support */
SelectLang:
action = selectlang(&locales); /* welcome to svardos, select your language */
if (action != MENUNEXT) goto Quit;
setenv("LANG", locales.lang, 1);
loadcp(&locales);
kittenclose(); /* reload NLS with new language */
kittenopen("INSTALL"); /* NLS support */
action = selectkeyb(&locales); /* what keyb layout should we use? */
if (action == MENUQUIT) goto Quit;
if (action == MENUPREV) goto SelectLang;
WelcomeScreen:
action = welcomescreen(); /* what svardos is, ask whether to run live dos or install */
if (action == MENUQUIT) goto Quit;
if (action == MENUPREV) goto SelectLang;
targetdrv = preparedrive(sourcedrv); /* what drive should we install from? check avail. space */
if (targetdrv == MENUQUIT) goto Quit;
if (targetdrv == MENUPREV) goto WelcomeScreen;
bootfilesgen(targetdrv, &locales); /* generate boot files and other configurations */
if (installpackages(targetdrv, sourcedrv, &locales) != 0) goto Quit; /* install packages */
/*localcfg();*/ /* show local params (currency, etc), and propose to change them (based on localcfg) */
/*netcfg();*/ /* basic networking config */
finalreboot(); /* remove the CD and reboot */
Quit:
kittenclose(); /* close NLS support */
video_clear(0x0700, 0, 0);
video_movecursor(0, 0);
return(0);
}