check alloc
[dos_imgv] / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <conio.h>
5 #include "video.h"
6 #include "mouse.h"
7 #include "imago2.h"
8 #include "chkalloc.h"
9
10 int switch_mode(int m);
11 int find_best_mode(int minx, int miny);
12 void drop_equiv_modes(void);
13
14 void display8(void);
15 void display16(void);
16 void display24(void);
17 void display32(void);
18 void (*display)(void);
19 void xor_cursor8(int x, int y);
20 void xor_cursor16(int x, int y);
21 void xor_cursor24(int x, int y);
22 void xor_cursor32(int x, int y);
23 void (*xor_cursor)(int x, int y);
24
25 int equiv_bpp(int a, int b);
26 int vmcmp(const void *a, const void *b);
27 int parse_args(int argc, char **argv);
28
29 static struct video_mode *vmode;
30 static struct video_mode *vmlist;
31 static int vmlist_size, cur_vm = -1;
32 static void *backbuf;
33
34 static int opt_width, opt_height;
35 static int opt_bpp = 16;
36 static const char *opt_fname;
37
38 static int redraw_pending = 1;
39 static int pan_x, pan_y, max_pan_x, max_pan_y;
40
41 static struct img_pixmap img;
42
43
44 int main(int argc, char **argv)
45 {
46         int i, num, modeidx;
47         struct video_mode *vmodes;
48
49         if(parse_args(argc, argv) == -1) {
50                 return 1;
51         }
52         if(!opt_fname) {
53                 fprintf(stderr, "please specify an image file to open\n");
54                 return 1;
55         }
56
57         if(init_video() == -1) {
58                 fprintf(stderr, "failed to initialize video\n");
59                 return 1;
60         }
61
62         /* populate and sort the usable video mode list */
63         num = num_video_modes();
64         vmodes = video_modes();
65
66         if(!(vmlist = chk_malloc(num * sizeof *vmlist))) {
67                 fprintf(stderr, "failed to allocate video mode list\n");
68                 return 1;
69         }
70         vmlist_size = 0;
71
72         for(i=0; i<num; i++) {
73                 if(equiv_bpp(opt_bpp, vmodes[i].bpp)) {
74                         vmlist[vmlist_size++] = vmodes[i];
75                 }
76         }
77         if(!vmlist_size) {
78                 fprintf(stderr, "no usable video modes found for %d bpp\n", opt_bpp);
79                 goto end;
80         }
81         qsort(vmlist, vmlist_size, sizeof *vmlist, vmcmp);
82         drop_equiv_modes();
83
84         img_init(&img);
85         if(img_load(&img, opt_fname) == -1) {
86                 fprintf(stderr, "failed to load image: %s\n", opt_fname);
87                 goto end;
88         }
89
90         switch(opt_bpp) {
91         case 15:
92         case 16:
93                 img_convert(&img, IMG_FMT_RGB565);
94                 break;
95         case 24:
96                 img_convert(&img, IMG_FMT_RGB24);
97                 break;
98         case 32:
99                 img_convert(&img, IMG_FMT_RGBA32);
100                 break;
101         }
102
103         init_mouse();
104
105         if(!opt_width) {
106                 opt_width = img.width;
107                 opt_height = img.height;
108         }
109         modeidx = find_best_mode(opt_width, opt_height);
110         if(switch_mode(modeidx) == -1) {
111                 goto end;
112         }
113
114         for(;;) {
115                 if(kbhit()) {
116                         int c = getch();
117                         switch(c) {
118                         case 27:
119                                 goto end;
120
121                         case '=':
122                                 if(cur_vm < vmlist_size - 1) {
123                                         switch_mode(cur_vm + 1);
124                                         redraw_pending = 1;
125                                 }
126                                 break;
127
128                         case '-':
129                                 if(cur_vm > 0) {
130                                         switch_mode(cur_vm - 1);
131                                         redraw_pending = 1;
132                                 }
133                                 break;
134                         }
135                 }
136
137                 if(mouse.active) {
138                         proc_mouse();
139                         if((mouse.dx | mouse.dy) && mouse.bn & 1) {
140                                 pan_x -= mouse.dx;
141                                 pan_y -= mouse.dy;
142                                 if(pan_x < 0) pan_x = 0;
143                                 if(pan_y < 0) pan_y = 0;
144                                 if(pan_x > max_pan_x) pan_x = max_pan_x;
145                                 if(pan_y > max_pan_y) pan_y = max_pan_y;
146                                 redraw_pending = 1;
147                         }
148                 }
149
150                 if(redraw_pending) {
151                         redraw_pending = 0;
152                         display();
153                         blit_frame(backbuf, FLIP_NOW);
154                 }
155         }
156 end:
157
158         img_destroy(&img);
159         chk_free(backbuf);
160         chk_free(vmlist);
161         if(cur_vm >= 0) {
162                 set_text_mode();
163         }
164         cleanup_video();
165         chk_check();
166         return 0;
167 }
168
169 int switch_mode(int m)
170 {
171         vmode = vmlist + m;
172         cur_vm = m;
173
174         chk_free(backbuf);
175         if(!(backbuf = chk_malloc(vmode->ysz * vmode->pitch))) {
176                 fprintf(stderr, "failed to allocate back buffer\n");
177                 return -1;
178         }
179
180         set_video_mode(find_video_mode(vmode->mode), 0);
181
182         switch(vmode->bpp) {
183         case 8:
184                 display = display8;
185                 xor_cursor = xor_cursor8;
186                 break;
187         case 15:
188         case 16:
189                 display = display16;
190                 xor_cursor = xor_cursor16;
191                 break;
192         case 24:
193                 display = display24;
194                 xor_cursor = xor_cursor24;
195                 break;
196         case 32:
197                 display = display32;
198                 xor_cursor = xor_cursor32;
199         }
200
201         if(mouse.active) {
202                 reset_mouse();
203                 set_mouse_bounds(0, 0, vmode->xsz, vmode->ysz);
204         }
205
206         max_pan_x = img.width - vmode->xsz;
207         max_pan_y = img.height - vmode->ysz;
208         if(max_pan_x < 0) max_pan_x = 0;
209         if(max_pan_y < 0) max_pan_y = 0;
210
211         if(pan_x > max_pan_x) pan_x = max_pan_x;
212         if(pan_y > max_pan_y) pan_y = max_pan_y;
213
214         return 0;
215 }
216
217 int find_best_mode(int minx, int miny)
218 {
219         int i;
220
221         for(i=0; i<vmlist_size; i++) {
222                 if(vmlist[i].xsz >= minx && vmlist[i].ysz >= miny) {
223                         return i;
224                 }
225         }
226         return vmlist_size - 1;
227 }
228
229 void drop_equiv_modes(void)
230 {
231         struct video_mode *vm, *end;
232
233         /* modes are sorted, so just keeping the last equiv of a pair, keeps the 
234          * preferred one
235          */
236         vm = vmlist + 1;
237         end = vmlist + vmlist_size;
238         while(vm < end) {
239                 if(vm->xsz == vm[-1].xsz && vm->ysz == vm[-1].ysz &&
240                                 equiv_bpp(vm->bpp, vm[-1].bpp)) {
241                         memmove(vm - 1, vm, (end - vm) * sizeof *vm);
242                         end--;
243                 } else {
244                         vm++;
245                 }
246         }
247         vmlist_size = end - vmlist;
248 }
249
250 void display8(void)
251 {
252 }
253
254 void display16(void)
255 {
256         int i, minx, miny;
257         uint16_t *dptr = backbuf;
258         uint16_t *sptr = (uint16_t*)img.pixels + pan_y * img.width + pan_x;
259
260         minx = img.width - pan_x;
261         if(minx > vmode->xsz) minx = vmode->xsz;
262         miny = img.height - pan_y;
263         if(miny > vmode->ysz) miny = vmode->ysz;
264
265         for(i=0; i<miny; i++) {
266                 memcpy(dptr, sptr, minx << 1);
267                 if(minx < vmode->xsz) {
268                         memset(dptr + minx, 0, (vmode->xsz - minx) << 1);
269                 }
270                 dptr += vmode->pitch >> 1;
271                 sptr += img.width;
272         }
273         if(miny < vmode->ysz) {
274                 memset(dptr, 0, (vmode->ysz - miny) * vmode->pitch);
275         }
276 }
277
278 void display24(void)
279 {
280 }
281
282 void display32(void)
283 {
284 }
285
286 void xor_cursor8(int x, int y)
287 {
288 }
289
290 void xor_cursor16(int x, int y)
291 {
292         unsigned short *sptr = (unsigned short*)backbuf + y * (vmode->pitch >> 1);
293         int i, x0, x1, y0, y1;
294
295         x0 = x - 7;
296         x1 = x + 7;
297         if(x0 < 0) x0 = 0;
298         if(x1 >= vmode->xsz) x1 = vmode->xsz - 1;
299         y0 = y - 7;
300         y1 = y + 7;
301         if(y0 < 0) y0 = 0;
302         if(y1 >= vmode->ysz) y1 = vmode->ysz - 1;
303
304         for(i=x0; i<=x1; i++) {
305                 sptr[i] ^= 0xffff;
306         }
307
308         sptr = (unsigned short*)backbuf + y0 * (vmode->pitch >> 1) + x;
309         for(i=y0; i<=y1; i++) {
310                 if(i != y) {
311                         *sptr ^= 0xffff;
312                 }
313                 sptr += vmode->pitch >> 1;
314         }
315 }
316
317 void xor_cursor24(int x, int y)
318 {
319 }
320
321 void xor_cursor32(int x, int y)
322 {
323 }
324
325 int equiv_bpp(int a, int b)
326 {
327         if(a == 15 || a == 16) {
328                 return b == 15 || b == 16 ? 1 : 0;
329         }
330         if(a == 24 || a == 32) {
331                 return b == 24 || b == 32 ? 1 : 0;
332         }
333         return a == b ? 1 : 0;
334 }
335
336 int vmcmp(const void *a, const void *b)
337 {
338         const struct video_mode *vma = a;
339         const struct video_mode *vmb = b;
340
341         return vma->ysz - vmb->ysz;
342 }
343
344 int parse_args(int argc, char **argv)
345 {
346         int i;
347         static const char *usage_fmt = "Usage: %s [options] <image file>\n"
348                 "Options:\n"
349                 " -s <WxH>: choose screen resolution if available\n"
350                 " -bpp <n>: video mode color depth (8, 15, 16, 24, 32)\n"
351                 " -h,-help: print usage information and exit\n";
352
353         for(i=1; i<argc; i++) {
354                 if(argv[i][0] == '-') {
355                         if(strcmp(argv[i], "-s") == 0) {
356                                 if(!argv[++i] || sscanf(argv[i], "%dx%d", &opt_width, &opt_height) != 2 ||
357                                                 opt_width <= 0 || opt_height <= 0) {
358                                         fprintf(stderr, "invalid resolution\n");
359                                         return -1;
360                                 }
361                         } else if(strcmp(argv[i], "-bpp") == 0) {
362                                 if(!argv[++i] || (opt_bpp = atoi(argv[i])) <= 0) {
363                                         fprintf(stderr, "invalid -bpp\n");
364                                         return -1;
365                                 }
366                         } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-help") == 0) {
367                                 printf(usage_fmt, argv[0]);
368                                 exit(0);
369                         } else {
370                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
371                                 printf(usage_fmt, argv[0]);
372                                 return -1;
373                         }
374                 } else {
375                         if(opt_fname) {
376                                 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
377                                 printf(usage_fmt, argv[0]);
378                                 return -1;
379                         }
380                         opt_fname = argv[i];
381                 }
382         }
383         return 0;
384 }