3 * $Id: bximage.c,v 1.33 2008/02/05 22:57:42 sshwarts Exp $
5 * Create empty hard disk or floppy disk images for bochs.
12 # include <winioctl.h>
28 #define HDIMAGE_HEADERS_ONLY 1
29 #include "../iodev/hdimage.h"
36 char bx_filename
[256];
38 typedef int (*WRITE_IMAGE
)(FILE*, Bit64u
);
40 typedef int (*WRITE_IMAGE_WIN32
)(HANDLE
, Bit64u
);
43 char *EOF_ERR
= "ERROR: End of input";
44 char *rcsid
= "$Id: bximage.c,v 1.33 2008/02/05 22:57:42 sshwarts Exp $";
45 char *divider
= "========================================================================";
47 /* menu data for choosing floppy/hard disk */
48 char *fdhd_menu
= "\nDo you want to create a floppy disk image or a hard disk image?\nPlease type hd or fd. ";
49 char *fdhd_choices
[] = { "fd", "hd" };
50 int fdhd_n_choices
= 2;
52 /* menu data for choosing floppy size */
53 char *fdsize_menu
= "\nChoose the size of floppy disk image to create, in megabytes.\nPlease type 0.16, 0.18, 0.32, 0.36, 0.72, 1.2, 1.44, 1.68, 1.72, or 2.88.\n ";
54 char *fdsize_choices
[] = { "0.16","0.18","0.32","0.36","0.72","1.2","1.44","1.68","1.72","2.88" };
55 int fdsize_n_choices
= 10;
57 /* menu data for choosing disk mode */
58 char *hdmode_menu
= "\nWhat kind of image should I create?\nPlease type flat, sparse or growing. ";
59 char *hdmode_choices
[] = {"flat", "sparse", "growing" };
60 int hdmode_n_choices
= 3;
62 void myexit (int code
)
65 printf ("\nPress any key to continue\n");
71 /* stolen from main.cc */
72 void bx_center_print (FILE *file
, char *line
, int maxwidth
)
76 imax
= (maxwidth
- strlen(line
)) >> 1;
77 for (i
=0; i
<imax
; i
++) fputc (' ', file
);
84 printf ("%s\n", divider
);
85 bx_center_print (stdout
, "bximage\n", 72);
86 bx_center_print (stdout
, "Disk Image Creation Tool for Bochs\n", 72);
87 bx_center_print (stdout
, rcsid
, 72);
88 printf ("\n%s\n", divider
);
91 /* this is how we crash */
98 /* check if the argument string is present in the list -
99 returns index on success, -1 on failure. */
100 int get_menu_index(char *arg
, int n_choices
, char *choice
[])
103 for (i
=0; i
<n_choices
; i
++) {
104 if (!strcmp (choice
[i
], arg
)) {
105 // matched, return the choice number
112 /* remove leading spaces, newline junk at end. returns pointer to
113 cleaned string, which is between s0 and the null */
115 clean_string (char *s0
)
119 /* find first nonblank */
122 /* truncate string at first non-alphanumeric */
124 while (isprint (*ptr
))
130 /* returns 0 on success, -1 on failure. The value goes into out. */
132 ask_int (char *prompt
, int min
, int max
, int the_default
, int *out
)
139 printf ("%s", prompt
);
140 printf ("[%d] ", the_default
);
141 if (!fgets (buffer
, sizeof(buffer
), stdin
))
143 clean
= clean_string (buffer
);
144 if (strlen(clean
) < 1) {
145 // empty line, use the default
149 illegal
= (1 != sscanf (buffer
, "%d", &n
));
150 if (illegal
|| n
<min
|| n
>max
) {
151 printf ("Your choice (%s) was not an integer between %d and %d.\n\n",
162 ask_menu (char *prompt
, int n_choices
, char *choice
[], int the_default
, int *out
)
169 printf ("%s", prompt
);
170 printf ("[%s] ", choice
[the_default
]);
171 if (!fgets (buffer
, sizeof(buffer
), stdin
))
173 clean
= clean_string (buffer
);
174 if (strlen(clean
) < 1) {
175 // empty line, use the default
179 for (i
=0; i
<n_choices
; i
++) {
180 if (!strcmp (choice
[i
], clean
)) {
181 // matched, return the choice number
186 printf ("Your choice (%s) did not match any of the choices:\n", clean
);
187 for (i
=0; i
<n_choices
; i
++) {
188 if (i
>0) printf (", ");
189 printf ("%s", choice
[i
]);
196 ask_yn (char *prompt
, int the_default
, int *out
)
202 printf ("%s", prompt
);
203 printf ("[%s] ", the_default
?"yes":"no");
204 if (!fgets (buffer
, sizeof(buffer
), stdin
))
206 clean
= clean_string (buffer
);
207 if (strlen(clean
) < 1) {
208 // empty line, use the default
212 switch (tolower(clean
[0])) {
213 case 'y': *out
=1; return 0;
214 case 'n': *out
=0; return 0;
216 printf ("Please type either yes or no.\n");
221 ask_string (char *prompt
, char *the_default
, char *out
)
226 printf ("%s", prompt
);
227 printf ("[%s] ", the_default
);
228 if (!fgets (buffer
, sizeof(buffer
), stdin
))
230 clean
= clean_string (buffer
);
231 if (strlen(clean
) < 1) {
232 // empty line, use the default
233 strcpy (out
, the_default
);
240 // fileset is like memset but for a file handle
241 void fileset(FILE * fp
, int c
, size_t n
)
243 #define BLOCK_SIZE (1024)
244 int block
[BLOCK_SIZE
];
245 size_t left_to_write
= n
;
247 memset(block
, c
, sizeof(block
));
249 while (left_to_write
> 0)
251 size_t write
= sizeof(block
);
252 if (write
> left_to_write
) write
= left_to_write
;
254 if (1 != fwrite(block
, write
, 1, fp
))
257 fatal ("ERROR: The disk image is not complete - could not write data block!");
260 left_to_write
-= write
;
265 /* Create a suited redolog header */
266 void make_redolog_header(redolog_header_t
*header
, const char* type
, Bit64u size
)
268 Bit32u entries
, extent_size
, bitmap_size
;
271 // Set standard header values
272 strcpy((char*)header
->standard
.magic
, STANDARD_HEADER_MAGIC
);
273 strcpy((char*)header
->standard
.type
, REDOLOG_TYPE
);
274 strcpy((char*)header
->standard
.subtype
, type
);
275 header
->standard
.version
= htod32(STANDARD_HEADER_VERSION
);
276 header
->standard
.header
= htod32(STANDARD_HEADER_SIZE
);
281 // Compute #entries and extent size values
283 static Bit32u flip
=0;
285 extent_size
= 8 * bitmap_size
* 512;
287 header
->specific
.catalog
= htod32(entries
);
288 header
->specific
.bitmap
= htod32(bitmap_size
);
289 header
->specific
.extent
= htod32(extent_size
);
291 maxsize
= (Bit64u
)entries
* (Bit64u
)extent_size
;
295 if(flip
&0x01) bitmap_size
*= 2;
297 } while (maxsize
< size
);
299 header
->specific
.disk
= htod64(size
);
302 /* produce a flat image file */
304 int make_flat_image_win32(HANDLE hFile
, Bit64u sec
)
307 DWORD dwCount
, errCode
;
311 SetLastError(NO_ERROR
);
312 mode
= COMPRESSION_FORMAT_DEFAULT
;
314 memset(buffer
, 0, 512);
315 WriteFile(hFile
, buffer
, 512, &dwCount
, NULL
); // set the first sector to 0, Win98 doesn't zero out the file
316 // if there is a write at/over the end
317 DeviceIoControl(hFile
, FSCTL_SET_COMPRESSION
, &mode
, sizeof(mode
), NULL
, 0, &dwCount
, NULL
);
318 pos
.u
.LowPart
= (unsigned long)((sec
- 1) << 9);
319 pos
.u
.HighPart
= (unsigned long)((sec
- 1) >> 23);
320 pos
.u
.LowPart
= SetFilePointer(hFile
, pos
.u
.LowPart
, &pos
.u
.HighPart
, FILE_BEGIN
);
321 memset(buffer
, 0, 512);
322 if ((pos
.u
.LowPart
== 0xffffffff && GetLastError() != NO_ERROR
) || !WriteFile(hFile
, buffer
, 512, &dwCount
, NULL
) || dwCount
!= 512)
324 errCode
= GetLastError();
326 if (errCode
== ERROR_DISK_FULL
) {
327 fatal ("\nERROR: Not enough space on disk for image!");
329 sprintf(buffer
, "\nERROR: Disk image creation failed with error code %i!", errCode
);
337 int make_flat_image(FILE *fp
, Bit64u sec
)
340 * seek to sec*512-1 and write a single character.
341 * can't just do: fseek(fp, 512*sec-1, SEEK_SET)
342 * because 512*sec may be too large for signed int.
346 /* temp <-- min(sec, 4194303)
347 * 4194303 is (int)(0x7FFFFFFF/512)
349 long temp
= (long)((sec
< 4194303) ? sec
: 4194303);
350 fseek(fp
, 512*temp
, SEEK_CUR
);
354 fseek(fp
, -1, SEEK_CUR
);
355 if (fputc('\0', fp
) == EOF
)
358 fatal ("\nERROR: The disk image is not complete! (image larger then free space?)");
363 /* produce a sparse image file */
364 int make_sparse_image(FILE *fp
, Bit64u sec
)
367 sparse_header_t header
;
369 size_t padtopagesize
;
371 memset(&header
, 0, sizeof(header
));
372 header
.magic
= htod32(SPARSE_HEADER_MAGIC
);
373 header
.version
= htod32(SPARSE_HEADER_VERSION
);
375 header
.pagesize
= htod32((1 << 10) * 32); // Use 32 KB Pages - could be configurable
376 numpages
= (sec
/ (dtoh32(header
.pagesize
) / 512)) + 1;
378 header
.numpages
= htod32((Bit32u
)numpages
);
379 header
.disk
= htod64(sec
* 512);
381 if (numpages
!= dtoh32(header
.numpages
))
384 fatal ("ERROR: The disk image is too large for a sparse image!");
385 // Could increase page size here.
386 // But note this only happens at 128 Terabytes!
389 if (fwrite(&header
, sizeof(header
), 1, fp
) != 1)
392 fatal ("ERROR: The disk image is not complete - could not write header!");
395 fileset(fp
, 0xff, 4 * dtoh32(header
.numpages
));
397 sizesofar
= SPARSE_HEADER_SIZE
+ (4 * dtoh32(header
.numpages
));
398 padtopagesize
= dtoh32(header
.pagesize
) - (sizesofar
& (dtoh32(header
.pagesize
) - 1));
400 fileset(fp
, 0, padtopagesize
);
405 /* produce a growing image file */
406 int make_growing_image(FILE *fp
, Bit64u sec
)
408 redolog_header_t header
;
409 Bit32u i
, not_allocated
= htod32(REDOLOG_PAGE_NOT_ALLOCATED
);
411 memset(&header
, 0, sizeof(header
));
412 make_redolog_header(&header
, REDOLOG_SUBTYPE_GROWING
, sec
* 512);
414 if (fwrite(&header
, sizeof(header
), 1, fp
) != 1)
417 fatal ("ERROR: The disk image is not complete - could not write header!");
420 for (i
=0; i
<dtoh32(header
.specific
.catalog
); i
++)
422 if (fwrite(¬_allocated
, sizeof(Bit32u
), 1, fp
) != 1)
425 fatal ("ERROR: The disk image is not complete - could not write catalog!");
432 /* produce the image file */
434 int make_image_win32 (Bit64u sec
, char *filename
, WRITE_IMAGE_WIN32 write_image
)
439 // check if it exists before trashing someone's disk image
440 hFile
= CreateFile(filename
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
441 if (hFile
!= INVALID_HANDLE_VALUE
) {
443 sprintf (buffer
, "\nThe disk image '%s' already exists. Are you sure you want to replace it?\nPlease type yes or no. ", filename
);
444 if (ask_yn (buffer
, 0, &confirm
) < 0)
447 fatal ("ERROR: Aborted");
451 // okay, now open it for writing
452 hFile
= CreateFile(filename
, GENERIC_WRITE
|GENERIC_READ
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
453 if (hFile
== INVALID_HANDLE_VALUE
) {
454 // attempt to print an error
456 sprintf (buffer
, "while opening '%s' for writing", filename
);
459 fatal ("ERROR: Could not write disk image");
462 printf ("\nWriting: [");
464 if( (*write_image
)(hFile
, sec
) != 0)
465 fatal ("ERROR: while writing disk image!");
467 printf ("] Done.\n");
473 int make_image (Bit64u sec
, char *filename
, WRITE_IMAGE write_image
)
478 // check if it exists before trashing someone's disk image
479 fp
= fopen (filename
, "r");
482 sprintf (buffer
, "\nThe disk image '%s' already exists. Are you sure you want to replace it?\nPlease type yes or no. ", filename
);
483 if (ask_yn (buffer
, 0, &confirm
) < 0)
486 fatal ("ERROR: Aborted");
490 // okay, now open it for writing
491 fp
= fopen (filename
, "w");
493 // attempt to print an error
495 sprintf (buffer
, "while opening '%s' for writing", filename
);
498 fatal ("ERROR: Could not write disk image");
501 printf ("\nWriting: [");
503 if( (*write_image
)(fp
, sec
) != 0)
504 fatal ("ERROR: while writing disk image!");
506 printf ("] Done.\n");
514 "Usage: bximage [options] [filename]\n\n"
515 "Supported options:\n"
516 " -fd create floppy image\n"
517 " -hd create hard disk image\n"
518 " -mode=... image mode (hard disks only)\n"
519 " -size=... image size in megabytes\n"
520 " -q quiet mode (don't prompt for user input)\n"
521 " --help display this help and exit\n\n");
524 int parse_cmdline (int argc
, char *argv
[])
535 while ((arg
< argc
) && (ret
== 1)) {
537 if (!strcmp ("--help", argv
[arg
]) || !strncmp ("/?", argv
[arg
], 2)) {
541 else if (!strcmp ("-fd", argv
[arg
])) {
545 else if (!strcmp ("-hd", argv
[arg
])) {
549 else if (!strncmp ("-mode=", argv
[arg
], 6)) {
550 if (bx_hdimage
== 1) {
551 bx_hdimagemode
= get_menu_index(&argv
[arg
][6], hdmode_n_choices
, hdmode_choices
);
552 if (bx_hdimagemode
< 0) {
553 printf ("Unknown image mode: %s\n\n", &argv
[arg
][6]);
557 printf ("Image mode option only supported for hard disks\n\n");
561 else if (!strncmp ("-size=", argv
[arg
], 6)) {
562 if (bx_hdimage
== 0) {
563 bx_fdsize_idx
= get_menu_index(&argv
[arg
][6], fdsize_n_choices
, fdsize_choices
);
564 if (bx_fdsize_idx
< 0) {
565 printf ("Unknown floppy image size: %s\n\n", &argv
[arg
][6]);
568 } else if (bx_hdimage
== 1) {
569 if (sscanf (&argv
[arg
][6], "%d", &bx_hdsize
) != 1) {
570 printf ("Error in hard disk image size argument: %s\n\n", &argv
[arg
][6]);
572 } else if ((bx_hdsize
< 1) || (bx_hdsize
> 32255)) {
573 printf ("Hard disk image size out of range\n\n");
577 printf ("Image type (fd/hd) not specified\n\n");
580 else if (!strcmp ("-q", argv
[arg
])) {
583 else if (argv
[arg
][0] == '-') {
584 printf ("Unknown option: %s\n\n", argv
[arg
]);
587 strcpy(bx_filename
, argv
[arg
]);
591 if (bx_hdimage
== -1) {
596 if (bx_hdimage
== 1) {
597 if (bx_hdimagemode
== -1) {
601 if (bx_hdsize
== -1) {
606 if (bx_fdsize_idx
== -1) {
611 if (!strlen(bx_filename
)) {
617 int main (int argc
, char *argv
[])
621 char bochsrc_line
[256];
623 WRITE_IMAGE write_function
=NULL
;
625 WRITE_IMAGE_WIN32 writefn_win32
=NULL
;
628 if (!parse_cmdline (argc
, argv
))
632 if (bx_interactive
) {
633 if (ask_menu (fdhd_menu
, fdhd_n_choices
, fdhd_choices
, bx_hdimage
, &bx_hdimage
) < 0)
638 int hdsize
, heads
=16, spt
=63;
641 if (bx_interactive
) {
642 if (ask_menu (hdmode_menu
, hdmode_n_choices
, hdmode_choices
, bx_hdimagemode
, &mode
) < 0)
644 if (ask_int ("\nEnter the hard disk size in megabytes, between 1 and 129023\n", 1, 129023, bx_hdsize
, &hdsize
) < 0)
647 mode
= bx_hdimagemode
;
650 cyl
= (unsigned int) (hdsize
*1024.0*1024.0/16.0/63.0/512.0);
651 assert (cyl
< 262144);
652 sectors
= cyl
*heads
*spt
;
653 printf ("\nI will create a '%s' hard disk image with\n", hdmode_choices
[mode
]);
654 printf (" cyl=%d\n", cyl
);
655 printf (" heads=%d\n", heads
);
656 printf (" sectors per track=%d\n", spt
);
657 printf (" total sectors=" FMT_LL
"d\n", sectors
);
658 printf (" total size=%.2f megabytes\n", (float)(Bit64s
)(sectors
/2)/1024.0);
659 if (bx_interactive
) {
660 if (!strlen(bx_filename
)) strcpy(bx_filename
, "c.img");
661 if (ask_string ("\nWhat should I name the image?\n", bx_filename
, filename
) < 0)
664 strcpy(filename
, bx_filename
);
667 sprintf (bochsrc_line
, "ata0-master: type=disk, path=\"%s\", mode=%s, cylinders=%d, heads=%d, spt=%d", filename
, hdmode_choices
[mode
], cyl
, heads
, spt
);
671 write_function
=make_sparse_image
;
674 write_function
=make_growing_image
;
678 writefn_win32
=make_flat_image_win32
;
680 write_function
=make_flat_image
;
684 int fdsize
, cyl
=0, heads
=0, spt
=0;
685 if (bx_interactive
) {
686 if (ask_menu (fdsize_menu
, fdsize_n_choices
, fdsize_choices
, bx_fdsize_idx
, &fdsize
) < 0)
689 fdsize
= bx_fdsize_idx
;
692 case 0: cyl
=40; heads
=1; spt
=8; break; /* 0.16 meg */
693 case 1: cyl
=40; heads
=1; spt
=9; break; /* 0.18 meg */
694 case 2: cyl
=40; heads
=2; spt
=8; break; /* 0.32 meg */
695 case 3: cyl
=40; heads
=2; spt
=9; break; /* 0.36 meg */
696 case 4: cyl
=80; heads
=2; spt
=9; break; /* 0.72 meg */
697 case 5: cyl
=80; heads
=2; spt
=15; break; /* 1.2 meg */
698 case 6: cyl
=80; heads
=2; spt
=18; break; /* 1.44 meg */
699 case 7: cyl
=80; heads
=2; spt
=21; break; /* 1.68 meg */
700 case 8: cyl
=82; heads
=2; spt
=21; break; /* 1.72 meg */
701 case 9: cyl
=80; heads
=2; spt
=36; break; /* 2.88 meg */
703 fatal ("ERROR: fdsize out of range");
705 sectors
= cyl
*heads
*spt
;
706 printf ("I will create a floppy image with\n");
707 printf (" cyl=%d\n", cyl
);
708 printf (" heads=%d\n", heads
);
709 printf (" sectors per track=%d\n", spt
);
710 printf (" total sectors=" FMT_LL
"d\n", sectors
);
711 printf (" total bytes=" FMT_LL
"d\n", sectors
*512);
712 if (bx_interactive
) {
713 if (!strlen(bx_filename
)) strcpy(bx_filename
, "a.img");
714 if (ask_string ("\nWhat should I name the image?\n", bx_filename
, filename
) < 0)
717 strcpy(filename
, bx_filename
);
719 sprintf (bochsrc_line
, "floppya: image=\"%s\", status=inserted", filename
);
721 write_function
=make_flat_image
;
724 fatal ("ERROR: Illegal disk size!");
725 if (strlen (filename
) < 1)
726 fatal ("ERROR: Illegal filename");
728 if (writefn_win32
!= NULL
) {
729 make_image_win32 (sectors
, filename
, writefn_win32
);
734 make_image (sectors
, filename
, write_function
);
736 printf ("\nI wrote " FMT_LL
"u bytes to ", sectors
*512);
737 printf ("%s.\n", filename
);
738 printf ("\nThe following line should appear in your bochsrc:\n");
739 printf (" %s\n", bochsrc_line
);
741 if (OpenClipboard(NULL
)) {
744 hgClip
= GlobalAlloc(GMEM_DDESHARE
, (strlen(bochsrc_line
) + 1));
745 strcpy((char *)GlobalLock(hgClip
), bochsrc_line
);
746 GlobalUnlock(hgClip
);
747 SetClipboardData(CF_TEXT
, hgClip
);
749 printf("(The line is stored in your windows clipboard, use CTRL-V to paste)\n");
754 // make picky compilers (c++, gcc) happy,
755 // even though we leave via 'myexit' just above