it werks!
[instimg] / src / main.c
index 5f4de5d..3717955 100644 (file)
@@ -1,47 +1,69 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <malloc.h>
 #include <windows.h>
+#include <winioctl.h>
 #include "widgets.h"
 #include "rawdisk.h"
 
+#define IMG_FNAME      "disk.img"
+
+static int instimg(const char *devpath);
+static void update_disks(void);
+static void onclick(struct wgt_widget *w);
+static void onmodify(struct wgt_widget *w);
+static void ck_usbonly_handler(struct wgt_widget *w);
+
+
 static struct wgt_window *win;
-static struct wgt_widget *lb_instto, *bn_inst, *bn_cancel, *cb_devs, *ck_usbonly;
+static struct wgt_widget *lb_instto, *bn_inst, *bn_cancel, *cb_devs, *ck_usbonly, *bn_cancel_inst;
 
 static struct rawdisk_device rawdev[64];
 static const char *items[64];
 static int num_rawdev;
 
-
-static void update_disks(void);
-static void onclick(struct wgt_widget *w);
-static void onmodify(struct wgt_widget *w);
-static void ck_usbonly_handler(struct wgt_widget *w);
+static FILE *imgfile;
+static long imgfile_size;
+static const char *err_noimg_fmt =
+       "Failed to open 256boss disk image: " IMG_FNAME "\n"
+       "%s\n"
+       "\n"
+       "Please run this installer from the 256boss distribution directory";
 
 
 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprevinst, char *cmdline, int showcmd)
 {
        int x, y;
        MSG msg;
+       char *msgbuf, *errmsg;
 
+       if(!(imgfile = fopen(IMG_FNAME, "rb"))) {
+               errmsg = strerror(errno);
+               msgbuf = alloca(strlen(err_noimg_fmt) + strlen(errmsg) + 1);
+               sprintf(msgbuf, err_noimg_fmt, errmsg);
+               MessageBox(0, msgbuf, "Missing disk image!", MB_OK | MB_ICONSTOP | MB_APPLMODAL);
+               return 1;
+       }
+       fseek(imgfile, 0, SEEK_END);
+       imgfile_size = ftell(imgfile);
 
-       if(!(win = wgt_window("256boss USB stick installer", 400, 300))) {
+       if(!(win = wgt_window("256boss USB stick installer", 420, 170))) {
+               fclose(imgfile);
                return 1;
        }
-       lb_instto = wgt_label(win, "Install to device:", 10, 10);
+       lb_instto = wgt_label(win, "Install to device:", 10, 20);
 
        x = wgt_xpos_after(lb_instto, WGT_AUTO);
-       cb_devs = wgt_combo(win, items, num_rawdev, 0, x, 10, 200, WGT_AUTO, onmodify);
+       cb_devs = wgt_combo(win, items, num_rawdev, 0, x, 20, 270, WGT_AUTO, onmodify);
 
        y = wgt_ypos_after(cb_devs, 16);
        ck_usbonly = wgt_checkbox(win, "only show USB devices", 1, x, y,
                        WGT_AUTO, WGT_AUTO, ck_usbonly_handler);
 
-       y = wgt_ypos_after(ck_usbonly, 16);
-       bn_inst = wgt_button(win, "Install", 10, y, WGT_AUTO, WGT_AUTO, onclick);
-
-       x = wgt_xpos_after(bn_inst, WGT_AUTO);
-       bn_cancel = wgt_button(win, "Cancel", x, y, WGT_AUTO, WGT_AUTO, onclick);
+       y = wgt_ypos_after(ck_usbonly, 32);
+       bn_inst = wgt_button(win, "Install", 70, y, 100, WGT_AUTO, onclick);
+       bn_cancel = wgt_button(win, "Cancel", 230, y, 100, WGT_AUTO, onclick);
 
        update_disks();
 
@@ -50,10 +72,133 @@ int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprevinst, char *cmdline, int show
                DispatchMessage(&msg);
        }
 
-       wgt_destroy_window(win);
+       fclose(imgfile);
+       wgt_free_window(win);
        return 0;
 }
 
+#define WIN_XPAD       10
+#define WIN_YPAD       10
+#define MIN_BS         (4 * 1024 * 1024)
+
+static int instimg(const char *devpath)
+{
+       MSG msg;
+       HANDLE hdev;
+       DWORD wrcount;
+       DISK_GEOMETRY geom;
+       int width, max_width, height, y, sz, blksz = 512, status = -1;
+       char *blkbuf;
+       struct wgt_window *win_inst;
+       struct wgt_widget *w, *pbar, *ptext;
+       struct wgt_rect *rect;
+       long num_blk_done, total_blk;
+       int progr;
+
+       if(!(hdev = CreateFile(devpath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       0, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0))) {
+               return -1;
+       }
+
+       if(rawdisk_eject(hdev) == 0) {
+               rawdisk_load(hdev);
+       }
+
+       if(DeviceIoControl(hdev, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, &geom, sizeof geom, &wrcount, 0)) {
+               blksz = geom.BytesPerSector;
+       }
+
+       while(blksz < MIN_BS) blksz <<= 1;
+
+       num_blk_done = 0;
+       total_blk = (imgfile_size + blksz - 1) / blksz;
+
+       if(!(blkbuf = malloc(blksz))) {
+               CloseHandle(hdev);
+               return -1;
+       }
+
+       max_width = 0;
+
+       win_inst = wgt_window("Installing ...", 320, 140);
+       wgt_quit_on_close(win_inst, 0);
+       
+       y = WIN_YPAD;
+       sprintf(blkbuf, "Device sector size: %d bytes", (int)geom.BytesPerSector);
+       w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
+       if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
+
+       y = wgt_ypos_after(w, 8);
+       sprintf(blkbuf, "Copying in blocks of: %d bytes", blksz);
+       w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
+       if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
+       
+       y = wgt_ypos_after(w, 8);
+       sprintf(blkbuf, "Image size: %ld bytes (%ld blocks)", imgfile_size, total_blk);
+       w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
+       if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
+       
+       y = wgt_ypos_after(w, 8);
+       ptext = wgt_label(win_inst, "-/- (0%)", WIN_XPAD, y);
+       
+       y = wgt_ypos_after(ptext, 8);
+       pbar = wgt_progbar(win_inst, WIN_XPAD, y, width - WIN_XPAD * 2, WGT_AUTO, 0);
+       if((width = wgt_xpos_after(pbar, WIN_XPAD)) > max_width) max_width = width;
+
+       y = wgt_ypos_after(pbar, 8);
+       bn_cancel_inst = wgt_button(win_inst, "Cancel", 100, y, WGT_AUTO, WGT_AUTO, onclick);
+       rect = wgt_widget_rect(bn_cancel_inst, 0);
+       wgt_move_widget(bn_cancel_inst, WIN_XPAD + (width - rect->width) / 2, rect->y);
+
+       height = wgt_ypos_after(bn_cancel_inst, WIN_YPAD);
+
+       max_width += GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
+       height += GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION);
+       wgt_resize_window(win_inst, max_width, height);
+
+       /* go through any pending messages once first, to allow the window to get drawn correctly */
+       while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+               if(msg.message == WM_CLOSE || msg.message == WM_DESTROY) {
+                       goto end;
+               }
+               TranslateMessage(&msg);
+               DispatchMessage(&msg);
+       }
+
+       rewind(imgfile);
+       while((sz = fread(blkbuf, 1, blksz, imgfile)) > 0) {
+               if(sz < blksz) {
+                       memset(blkbuf + sz, 0, blksz - sz);
+               }
+               SetFilePointer(hdev, num_blk_done * blksz, 0, FILE_BEGIN);
+               WriteFile(hdev, blkbuf, blksz, &wrcount, 0);
+
+               num_blk_done++;
+               progr = num_blk_done * 100 / total_blk;
+               sprintf(blkbuf, "%ld/%ld (%d%%)", num_blk_done, total_blk, progr);
+               wgt_set_text(ptext, blkbuf);
+               wgt_set_progress(pbar, progr);
+
+               while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+                       if(msg.message == WM_CLOSE || msg.message == WM_DESTROY) {
+                               goto end;
+                       }
+                       TranslateMessage(&msg);
+                       DispatchMessage(&msg);
+               }
+       }
+
+       rawdisk_refresh(hdev);
+
+       status = 0;
+end:
+       wgt_free_window(win_inst);
+
+       free(blkbuf);
+       CloseHandle(hdev);
+       return status;
+}
+
 static int isusbdev(struct rawdisk_device *dev)
 {
        char *ptr = dev->path;
@@ -86,11 +231,14 @@ static void update_disks(void)
        }
 
        if(wgt_checkbox_checked(ck_usbonly)) {
-               for(i=0; i<num_rawdev; i++) {
+               i = 0;
+               while(i < num_rawdev) {
                        if(!isusbdev(rawdev + i)) {
                                free(rawdev[i].name);
                                free(rawdev[i].path);
                                rawdev[i] = rawdev[--num_rawdev];
+                       } else {
+                               i++;
                        }
                }
        }
@@ -110,14 +258,59 @@ static void update_disks(void)
        }
 }
 
+static const char *verify_fmt = \
+       "You are about to install 256boss to: %s\n"
+       "\n"
+       "Device path:\n"
+       "%s\n"
+       "\n"
+       "Any existing data in the selected device will be lost!\n"
+       "Please verify you have selected the intended device.\n"
+       "\n"
+       "Are you certain you wish to proceed with the installation?\n";
+
+static const char *success_str = \
+       "Installation complete!\n"
+       "\n"
+       "If the 256boss partition doesn't show up immediately,\n"
+       "please eject and re-attach the device\n";
+
 static void onclick(struct wgt_widget *w)
 {
+       int sel, len;
+       unsigned int flags;
+       char *msgbuf;
+       static int inst_running;
+
        if(w == bn_inst) {
-               MessageBox(0, "clicked", "clicked", MB_OK);
+               if(inst_running) return;
+
+               sel = wgt_combo_selected(cb_devs);
+               if(sel < 0 || sel >= num_rawdev) {
+                       return;
+               }
+
+               len = strlen(verify_fmt) + strlen(rawdev[sel].name) + strlen(rawdev[sel].path);
+               msgbuf = alloca(len + 1);
+               sprintf(msgbuf, verify_fmt, rawdev[sel].name, rawdev[sel].path);
+
+               flags = MB_YESNO | MB_ICONWARNING | MB_APPLMODAL | MB_SETFOREGROUND;
+               if(MessageBox(0, msgbuf, "Proceed with installation?", flags) == IDYES) {
+                       inst_running = 1;
+                       if(instimg(rawdev[sel].path) == -1) {
+                               MessageBox(0, "Installation failed", 0, MB_OK | MB_ICONSTOP | MB_APPLMODAL | MB_SETFOREGROUND);
+                       } else {
+                               MessageBox(0, success_str, "Done", MB_OK | MB_ICONASTERISK | MB_APPLMODAL | MB_SETFOREGROUND);
+                       }
+                       inst_running = 0;
+               }
+
        } else if(w == bn_cancel) {
                PostQuitMessage(0);
-       } else if(w == ck_usbonly) {
-               update_disks();
+
+       } else if(w == bn_cancel_inst) {
+               struct wgt_window *win = wgt_widget_window(w);
+               PostMessage(wgt_window_handle(win), WM_CLOSE, 0, 0);
        }
 }
 
@@ -134,3 +327,9 @@ static void ck_usbonly_handler(struct wgt_widget *w)
 {
        update_disks();
 }
+
+static int close_action(struct wgt_window *w)
+{
+       if(w != win) return 0;
+       return 1;
+}
\ No newline at end of file