+#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)) == INVALID_HANDLE_VALUE) {
+ DWORD err = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0,
+ &fail_err_str, 0, 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);
+ fail_err_str = "failed to allocate block buffer";
+ 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);
+
+ 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);
+ if(!WriteFile(hdev, blkbuf, blksz, &wrcount, 0)) {
+ DWORD err = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0,
+ &fail_err_str, 0, 0);
+ goto end;
+ }
+
+ 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;
+}
+