37179553f9bdd58c9ae9e251a58a59da0cf59d5d
[instimg] / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <malloc.h>
5 #include <windows.h>
6 #include <winioctl.h>
7 #include "widgets.h"
8 #include "rawdisk.h"
9
10 #define IMG_FNAME       "disk.img"
11
12 static int instimg(const char *devpath);
13 static void update_disks(void);
14 static void onclick(struct wgt_widget *w);
15 static void onmodify(struct wgt_widget *w);
16 static void ck_usbonly_handler(struct wgt_widget *w);
17
18
19 static struct wgt_window *win;
20 static struct wgt_widget *lb_instto, *bn_inst, *bn_cancel, *cb_devs, *ck_usbonly, *bn_cancel_inst;
21
22 static struct rawdisk_device rawdev[64];
23 static const char *items[64];
24 static int num_rawdev;
25
26 static FILE *imgfile;
27 static long imgfile_size;
28 static const char *err_noimg_fmt =
29         "Failed to open 256boss disk image: " IMG_FNAME "\n"
30         "%s\n"
31         "\n"
32         "Please run this installer from the 256boss distribution directory";
33
34
35 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprevinst, char *cmdline, int showcmd)
36 {
37         int x, y;
38         MSG msg;
39         char *msgbuf, *errmsg;
40
41         if(!(imgfile = fopen(IMG_FNAME, "rb"))) {
42                 errmsg = strerror(errno);
43                 msgbuf = alloca(strlen(err_noimg_fmt) + strlen(errmsg) + 1);
44                 sprintf(msgbuf, err_noimg_fmt, errmsg);
45                 MessageBox(0, msgbuf, "Missing disk image!", MB_OK | MB_ICONSTOP | MB_APPLMODAL);
46                 return 1;
47         }
48         fseek(imgfile, 0, SEEK_END);
49         imgfile_size = ftell(imgfile);
50
51         if(!(win = wgt_window("256boss USB stick installer", 420, 170))) {
52                 fclose(imgfile);
53                 return 1;
54         }
55         lb_instto = wgt_label(win, "Install to device:", 10, 20);
56
57         x = wgt_xpos_after(lb_instto, WGT_AUTO);
58         cb_devs = wgt_combo(win, items, num_rawdev, 0, x, 20, 270, WGT_AUTO, onmodify);
59
60         y = wgt_ypos_after(cb_devs, 16);
61         ck_usbonly = wgt_checkbox(win, "only show USB devices", 1, x, y,
62                         WGT_AUTO, WGT_AUTO, ck_usbonly_handler);
63
64         y = wgt_ypos_after(ck_usbonly, 32);
65         bn_inst = wgt_button(win, "Install", 70, y, 100, WGT_AUTO, onclick);
66         bn_cancel = wgt_button(win, "Cancel", 230, y, 100, WGT_AUTO, onclick);
67
68         update_disks();
69
70         while(GetMessage(&msg, 0, 0, 0)) {
71                 TranslateMessage(&msg);
72                 DispatchMessage(&msg);
73         }
74
75         fclose(imgfile);
76         wgt_free_window(win);
77         return 0;
78 }
79
80 #define WIN_XPAD        10
81 #define WIN_YPAD        10
82 #define MIN_BS          (4 * 1024 * 1024)
83
84 static int instimg(const char *devpath)
85 {
86         MSG msg;
87         HANDLE hdev;
88         DWORD wrcount;
89         DISK_GEOMETRY geom;
90         int width, max_width, height, y, sz, blksz = 512, status = -1;
91         char *blkbuf;
92         struct wgt_window *win_inst;
93         struct wgt_widget *w, *pbar, *ptext;
94         struct wgt_rect *rect;
95         long num_blk_done, total_blk;
96         int progr;
97
98         if(!(hdev = CreateFile(devpath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
99                         0, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0))) {
100                 return -1;
101         }
102
103         if(rawdisk_eject(hdev) == 0) {
104                 rawdisk_load(hdev);
105         }
106
107         if(DeviceIoControl(hdev, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, &geom, sizeof geom, &wrcount, 0)) {
108                 blksz = geom.BytesPerSector;
109         }
110
111         while(blksz < MIN_BS) blksz <<= 1;
112
113         num_blk_done = 0;
114         total_blk = (imgfile_size + blksz - 1) / blksz;
115
116         if(!(blkbuf = malloc(blksz))) {
117                 CloseHandle(hdev);
118                 return -1;
119         }
120
121         max_width = 0;
122
123         win_inst = wgt_window("Installing ...", 320, 140);
124         wgt_quit_on_close(win_inst, 0);
125         
126         y = WIN_YPAD;
127         sprintf(blkbuf, "Device sector size: %d bytes", (int)geom.BytesPerSector);
128         w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
129         if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
130
131         y = wgt_ypos_after(w, 8);
132         sprintf(blkbuf, "Copying in blocks of: %d bytes", blksz);
133         w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
134         if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
135         
136         y = wgt_ypos_after(w, 8);
137         sprintf(blkbuf, "Image size: %ld bytes (%ld blocks)", imgfile_size, total_blk);
138         w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
139         if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
140         
141         y = wgt_ypos_after(w, 8);
142         ptext = wgt_label(win_inst, "-/- (0%)", WIN_XPAD, y);
143         
144         y = wgt_ypos_after(ptext, 8);
145         pbar = wgt_progbar(win_inst, WIN_XPAD, y, width - WIN_XPAD * 2, WGT_AUTO, 0);
146         if((width = wgt_xpos_after(pbar, WIN_XPAD)) > max_width) max_width = width;
147
148         y = wgt_ypos_after(pbar, 8);
149         bn_cancel_inst = wgt_button(win_inst, "Cancel", 100, y, WGT_AUTO, WGT_AUTO, onclick);
150         rect = wgt_widget_rect(bn_cancel_inst, 0);
151         wgt_move_widget(bn_cancel_inst, WIN_XPAD + (width - rect->width) / 2, rect->y);
152
153         height = wgt_ypos_after(bn_cancel_inst, WIN_YPAD);
154
155         max_width += GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
156         height += GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION);
157         wgt_resize_window(win_inst, max_width, height);
158
159         /* go through any pending messages once first, to allow the window to get drawn correctly */
160         while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
161                 if(msg.message == WM_CLOSE || msg.message == WM_DESTROY) {
162                         goto end;
163                 }
164                 TranslateMessage(&msg);
165                 DispatchMessage(&msg);
166         }
167
168         rewind(imgfile);
169         while((sz = fread(blkbuf, 1, blksz, imgfile)) > 0) {
170                 if(sz < blksz) {
171                         memset(blkbuf + sz, 0, blksz - sz);
172                 }
173                 SetFilePointer(hdev, num_blk_done * blksz, 0, FILE_BEGIN);
174                 WriteFile(hdev, blkbuf, blksz, &wrcount, 0);
175
176                 num_blk_done++;
177                 progr = num_blk_done * 100 / total_blk;
178                 sprintf(blkbuf, "%ld/%ld (%d%%)", num_blk_done, total_blk, progr);
179                 wgt_set_text(ptext, blkbuf);
180                 wgt_set_progress(pbar, progr);
181
182                 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
183                         if(msg.message == WM_CLOSE || msg.message == WM_DESTROY) {
184                                 goto end;
185                         }
186                         TranslateMessage(&msg);
187                         DispatchMessage(&msg);
188                 }
189         }
190
191         rawdisk_refresh(hdev);
192
193         status = 0;
194 end:
195         wgt_free_window(win_inst);
196
197         free(blkbuf);
198         CloseHandle(hdev);
199         return status;
200 }
201
202 static int isusbdev(struct rawdisk_device *dev)
203 {
204         char *ptr = dev->path;
205         while(*ptr) {
206                 int i;
207                 char buf[3];
208                 for(i=0; i<3; i++) {
209                         buf[i] = tolower(ptr[i]);
210                 }
211                 if(memcmp(buf, "usb", 3) == 0) {
212                         return 1;
213                 }
214                 ptr++;
215         }
216         return 0;
217 }
218
219 static void update_disks(void)
220 {
221         int i;
222
223         for(i=0; i<num_rawdev; i++) {
224                 free(rawdev[i].name);
225                 free(rawdev[i].path);
226         }
227
228         if((num_rawdev = rawdisk_detect(rawdev, sizeof rawdev / sizeof *rawdev)) == -1) {
229                 MessageBox(0, "Failed to detect storage devices!", 0, MB_OK);
230                 exit(1);
231         }
232
233         if(wgt_checkbox_checked(ck_usbonly)) {
234                 i = 0;
235                 while(i < num_rawdev) {
236                         if(!isusbdev(rawdev + i)) {
237                                 free(rawdev[i].name);
238                                 free(rawdev[i].path);
239                                 rawdev[i] = rawdev[--num_rawdev];
240                         } else {
241                                 i++;
242                         }
243                 }
244         }
245
246         for(i=0; i<num_rawdev; i++) {
247                 items[i] = rawdev[i].name;
248         }
249
250         wgt_combo_setitems(cb_devs, items, num_rawdev);
251
252         if(num_rawdev) {
253                 wgt_enable_widget(cb_devs);
254                 wgt_enable_widget(bn_inst);
255         } else {
256                 wgt_disable_widget(cb_devs);
257                 wgt_disable_widget(bn_inst);
258         }
259 }
260
261 static const char *verify_fmt = \
262         "You are about to install 256boss to: %s\n"
263         "\n"
264         "Device path:\n"
265         "%s\n"
266         "\n"
267         "Any existing data in the selected device will be lost!\n"
268         "Please verify you have selected the intended device.\n"
269         "\n"
270         "Are you certain you wish to proceed with the installation?\n";
271
272 static const char *success_str = \
273         "Installation complete!\n"
274         "\n"
275         "If the 256boss partition doesn't show up immediately,\n"
276         "please eject and re-attach the device\n";
277
278 static void onclick(struct wgt_widget *w)
279 {
280         int sel, len;
281         unsigned int flags;
282         char *msgbuf;
283         static int inst_running;
284
285         if(w == bn_inst) {
286                 if(inst_running) return;
287
288                 sel = wgt_combo_selected(cb_devs);
289                 if(sel < 0 || sel >= num_rawdev) {
290                         return;
291                 }
292
293                 len = strlen(verify_fmt) + strlen(rawdev[sel].name) + strlen(rawdev[sel].path);
294                 msgbuf = alloca(len + 1);
295                 sprintf(msgbuf, verify_fmt, rawdev[sel].name, rawdev[sel].path);
296
297                 flags = MB_YESNO | MB_ICONWARNING | MB_APPLMODAL | MB_SETFOREGROUND;
298                 if(MessageBox(0, msgbuf, "Proceed with installation?", flags) == IDYES) {
299                         inst_running = 1;
300                         if(instimg(rawdev[sel].path) == -1) {
301                                 MessageBox(0, "Installation failed", 0, MB_OK | MB_ICONSTOP | MB_APPLMODAL | MB_SETFOREGROUND);
302                         } else {
303                                 MessageBox(0, success_str, "Done", MB_OK | MB_ICONASTERISK | MB_APPLMODAL | MB_SETFOREGROUND);
304                         }
305                         inst_running = 0;
306                 }
307
308         } else if(w == bn_cancel) {
309                 PostQuitMessage(0);
310
311         } else if(w == bn_cancel_inst) {
312                 struct wgt_window *win = wgt_widget_window(w);
313                 PostMessage(wgt_window_handle(win), WM_CLOSE, 0, 0);
314         }
315 }
316
317 static void onmodify(struct wgt_widget *w)
318 {
319         int sel = wgt_combo_selected(w);
320         if(sel >= 0) {
321                 const char *selstr = wgt_get_combo_item(w, sel);
322                 MessageBox(0, selstr, "selected item", MB_OK);
323         }
324 }
325
326 static void ck_usbonly_handler(struct wgt_widget *w)
327 {
328         update_disks();
329 }
330
331 static int close_action(struct wgt_window *w)
332 {
333         if(w != win) return 0;
334         return 1;
335 }