5f1db6ed1de0e4e4ae73af4b19cacaf0a83a42d7
[reactos.git] / reactos / sdk / tools / cdmake / cdmake.c
1 /*
2 * CD-ROM Maker
3 * by Philip J. Erdelsky
4 * pje@acm.org
5 * http://alumnus.caltech.edu/~pje/
6 *
7 * http://alumnus.caltech.edu/~pje/cdmake.txt
8 *
9 * According to his website, this file was released into the public domain
10 * by Philip J. Erdelsky.
11 */
12 /*
13 * COPYRIGHT: See COPYING in the top level directory
14 * PROJECT: ReactOS CD-ROM Maker
15 * FILE: tools/cdmake/cdmake.c
16 * PURPOSE: CD-ROM Premastering Utility
17 * PROGRAMMERS: Eric Kohl
18 * Casper S. Hornstrup
19 * Filip Navara
20 * Magnus Olsen
21 * Hermes Belusca-Maito
22 *
23 * HISTORY:
24 *
25 * ElTorito-Support
26 * by Eric Kohl
27 *
28 * Linux port
29 * by Casper S. Hornstrup
30 * chorns@users.sourceforge.net
31 *
32 * Joliet support
33 * by Filip Navara
34 * xnavara@volny.cz
35 * Limitations:
36 * - No Joliet file name validations
37 * - Very bad ISO file name generation
38 *
39 * Convert long filename to ISO9660 file name by Magnus Olsen
40 * magnus@greatlord.com
41 */
42
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <sys/stat.h>
46 #include <stdarg.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #ifdef _WIN32
50 # define WIN32_LEAN_AND_MEAN
51 # include <windows.h>
52 # include <io.h>
53 # include <dos.h>
54 # ifdef _MSC_VER
55 # define R_OK 4
56 # endif
57 #else
58 # if defined(__FreeBSD__) || defined(__APPLE__)
59 # include <sys/uio.h>
60 # else
61 # include <sys/io.h>
62 # endif // __FreeBSD__
63 # include <errno.h>
64 # include <sys/types.h>
65 # include <dirent.h>
66 # include <unistd.h>
67 # define TRUE 1
68 # define FALSE 0
69 #endif // _WIN32
70 #include <ctype.h>
71 #include <time.h>
72 #include "config.h"
73 #include "dirhash.h"
74
75 // FIXME! FIXME! Do it in a portable way!!
76 typedef unsigned char BYTE;
77 typedef unsigned short WORD;
78 typedef unsigned long DWORD;
79 typedef int BOOL;
80
81 // file system parameters
82
83 #define MAX_LEVEL 8
84 #define MAX_NAME_LENGTH 64
85 #define MAX_CDNAME_LENGTH 8
86 #define MAX_EXTENSION_LENGTH 10
87 #define MAX_CDEXTENSION_LENGTH 3
88 #define SECTOR_SIZE 2048
89 #define BUFFER_SIZE (8 * SECTOR_SIZE)
90
91 #define HIDDEN_FLAG 1
92 #define DIRECTORY_FLAG 2
93
94
95 struct cd_image
96 {
97 FILE *file;
98 DWORD sector; // sector to receive next byte
99 int offset; // offset of next byte in sector
100 int count; // number of bytes in buffer
101 char filespecs[128];
102 BYTE *buffer;
103 };
104
105 typedef struct _boot_validation_header
106 {
107 BYTE header_id;
108 BYTE platform_id;
109 } BOOT_VALIDATION_HEADER, *PBOOT_VALIDATION_HEADER;
110
111 typedef struct boot_entry
112 {
113 struct boot_entry *next_entry;
114 BYTE boot_id;
115 BYTE boot_emu_type;
116 WORD load_segment;
117 BYTE system_type;
118 WORD sector_count; // boot_image_size
119 DWORD load_rba; // boot_image_sector
120 // BYTE unused[20];
121 char bootimage[512];
122 } BOOT_ENTRY, *PBOOT_ENTRY;
123
124 typedef struct boot_header
125 {
126 struct boot_header *next_header;
127 BYTE header_id;
128 BYTE platform_id;
129 WORD num_entries;
130 // char id_string[28];
131 PBOOT_ENTRY entry_list;
132 } BOOT_HEADER, *PBOOT_HEADER;
133
134 typedef struct date_and_time
135 {
136 BYTE second;
137 BYTE minute;
138 BYTE hour;
139 BYTE day;
140 BYTE month;
141 WORD year;
142 } DATE_AND_TIME, *PDATE_AND_TIME;
143
144 typedef struct directory_record
145 {
146 struct directory_record *next_in_directory;
147 struct directory_record *next_in_path_table; /* directory record only */
148 struct directory_record *next_in_memory;
149 struct directory_record *first_record; /* directory record only */
150 struct directory_record *parent;
151 BYTE flags;
152 char name[MAX_NAME_LENGTH+1];
153 char name_on_cd[MAX_CDNAME_LENGTH+1];
154 char extension[MAX_EXTENSION_LENGTH+1];
155 char extension_on_cd[MAX_CDEXTENSION_LENGTH+1];
156 char *joliet_name;
157 const char *orig_name;
158 DATE_AND_TIME date_and_time;
159 DWORD sector;
160 DWORD size;
161 DWORD joliet_sector;
162 DWORD joliet_size;
163 unsigned level; /* directory record only */
164 WORD path_table_index; /* directory record only */
165 } DIR_RECORD, *PDIR_RECORD;
166
167 typedef enum directory_record_type
168 {
169 DOT_RECORD,
170 DOT_DOT_RECORD,
171 SUBDIRECTORY_RECORD,
172 FILE_RECORD
173 } DIR_RECORD_TYPE, *PDIR_RECORD_TYPE;
174
175
176 PDIR_RECORD sort_linked_list(PDIR_RECORD,
177 unsigned, int (*)(PDIR_RECORD, PDIR_RECORD));
178
179
180 static char DIRECTORY_TIMESTAMP[] = "~Y$'KOR$.3K&";
181
182 static struct cd_image cd;
183 DIR_RECORD root;
184
185 char volume_label[32];
186 char source[512];
187 char *end_source;
188
189 enum {QUIET, NORMAL, VERBOSE} verbosity;
190 BOOL show_progress;
191
192 BOOL scan_files_only = FALSE;
193 // BOOL compute_crc = FALSE;
194
195 /*
196 * ISO 9660 information
197 */
198 DWORD size_limit;
199 BOOL accept_punctuation_marks;
200
201 DWORD total_sectors;
202 DWORD path_table_size;
203 DWORD little_endian_path_table_sector;
204 DWORD big_endian_path_table_sector;
205
206
207 /*
208 * Stats
209 */
210 DWORD number_of_files;
211 DWORD bytes_in_files;
212 DWORD unused_bytes_at_ends_of_files;
213 DWORD number_of_directories;
214 DWORD bytes_in_directories;
215
216 struct target_dir_hash specified_files;
217
218
219 /*
220 * El-Torito boot information
221 */
222 BOOL eltorito; // TRUE/FALSE: bootable/non-bootable CD-ROM
223 BOOL multi_boot; // TRUE/FALSE: multi/single-boot CD-ROM
224 DWORD boot_catalog_sector;
225 BOOT_VALIDATION_HEADER boot_validation_header;
226 BOOT_ENTRY default_boot_entry;
227 PBOOT_HEADER boot_header_list;
228
229 /*
230 * Joliet information
231 */
232 BOOL joliet;
233 DWORD joliet_path_table_size;
234 DWORD joliet_little_endian_path_table_sector;
235 DWORD joliet_big_endian_path_table_sector;
236
237 /*
238 * UDF information
239 */
240 BOOL make_bridged_udf = TRUE; // TRUE for "UDF-Bridge" (aka. UDF/ISO); FALSE for pure UDF.
241
242
243 /*-----------------------------------------------------------------------------
244 This function edits a 32-bit unsigned number into a comma-delimited form, such
245 as 4,294,967,295 for the largest possible number, and returns a pointer to a
246 static buffer containing the result. It suppresses leading zeros and commas,
247 but optionally pads the result with blanks at the left so the result is always
248 exactly 13 characters long (excluding the terminating zero).
249
250 CAUTION: A statement containing more than one call on this function, such as
251 printf("%s, %s", edit_with_commas(a), edit_with_commas(b)), will produce
252 incorrect results because all calls use the same static bufffer.
253 -----------------------------------------------------------------------------*/
254
255 static char *edit_with_commas(DWORD x, BOOL pad)
256 {
257 static char s[14];
258 unsigned i = 13;
259 do
260 {
261 if (i % 4 == 2) s[--i] = ',';
262 s[--i] = (char)(x % 10 + '0');
263 } while ((x/=10) != 0);
264 if (pad)
265 {
266 while (i > 0) s[--i] = ' ';
267 }
268 return s + i;
269 }
270
271 /*-----------------------------------------------------------------------------
272 This function releases all allocated memory blocks.
273 -----------------------------------------------------------------------------*/
274
275 static void release_memory(void)
276 {
277 while (boot_header_list)
278 {
279 PBOOT_HEADER next_header = boot_header_list->next_header;
280
281 while (boot_header_list->entry_list)
282 {
283 PBOOT_ENTRY next_entry = boot_header_list->entry_list->next_entry;
284 free(boot_header_list->entry_list);
285 boot_header_list->entry_list = next_entry;
286 }
287
288 free(boot_header_list);
289 boot_header_list = next_header;
290 }
291
292 while (root.next_in_memory != NULL)
293 {
294 PDIR_RECORD next = root.next_in_memory->next_in_memory;
295 if (joliet)
296 free(root.next_in_memory->joliet_name);
297 free(root.next_in_memory);
298 root.next_in_memory = next;
299 }
300 if (joliet)
301 free(root.joliet_name);
302
303 if (cd.buffer != NULL)
304 {
305 free(cd.buffer);
306 cd.buffer = NULL;
307 }
308 }
309
310 /*-----------------------------------------------------------------------------
311 This function edits and displays an error message and then jumps back to the
312 error exit point in main().
313 -----------------------------------------------------------------------------*/
314
315 static void error_exit(const char* fmt, ...)
316 {
317 va_list arg;
318
319 va_start(arg, fmt);
320 vprintf(fmt, arg);
321 va_end(arg);
322 printf("\n");
323 if (cd.file != NULL)
324 fclose(cd.file);
325 release_memory();
326 exit(1);
327 }
328
329 /*-----------------------------------------------------------------------------
330 This function, which is called only on the second pass, and only when the
331 buffer is not empty, flushes the buffer to the CD-ROM image.
332 -----------------------------------------------------------------------------*/
333
334 static void flush_buffer(void)
335 {
336 if (fwrite(cd.buffer, cd.count, 1, cd.file) < 1)
337 error_exit("File write error");
338 cd.count = 0;
339 if (show_progress)
340 {
341 printf("\r%s ",
342 edit_with_commas((total_sectors - cd.sector) * SECTOR_SIZE, TRUE));
343 }
344 }
345
346 /*-----------------------------------------------------------------------------
347 This function writes a single byte to the CD-ROM image. On the first pass (in
348 which cd.handle < 0), it does not actually write anything but merely updates
349 the file pointer as though the byte had been written.
350 -----------------------------------------------------------------------------*/
351
352 static void write_byte(BYTE x)
353 {
354 if (cd.file != NULL)
355 {
356 cd.buffer[cd.count] = x;
357 if (++cd.count == BUFFER_SIZE)
358 flush_buffer();
359 }
360 if (++cd.offset == SECTOR_SIZE)
361 {
362 cd.sector++;
363 cd.offset = 0;
364 }
365 }
366
367 /*-----------------------------------------------------------------------------
368 These functions write a word or double word to the CD-ROM image with the
369 specified endianity.
370 -----------------------------------------------------------------------------*/
371
372 static void write_little_endian_word(WORD x)
373 {
374 write_byte((BYTE)x);
375 write_byte((BYTE)(x >> 8));
376 }
377
378 static void write_big_endian_word(WORD x)
379 {
380 write_byte((BYTE)(x >> 8));
381 write_byte((BYTE)x);
382 }
383
384 static void write_both_endian_word(WORD x)
385 {
386 write_little_endian_word(x);
387 write_big_endian_word(x);
388 }
389
390 static void write_little_endian_dword(DWORD x)
391 {
392 write_byte((BYTE)x);
393 write_byte((BYTE)(x >> 8));
394 write_byte((BYTE)(x >> 16));
395 write_byte((BYTE)(x >> 24));
396 }
397
398 static void write_big_endian_dword(DWORD x)
399 {
400 write_byte((BYTE)(x >> 24));
401 write_byte((BYTE)(x >> 16));
402 write_byte((BYTE)(x >> 8));
403 write_byte((BYTE)x);
404 }
405
406 static void write_both_endian_dword(DWORD x)
407 {
408 write_little_endian_dword(x);
409 write_big_endian_dword(x);
410 }
411
412 /*-----------------------------------------------------------------------------
413 This function writes enough zeros to fill out the end of a sector, and leaves
414 the file pointer at the beginning of the next sector. If the file pointer is
415 already at the beginning of a sector, it writes nothing.
416 -----------------------------------------------------------------------------*/
417
418 static void fill_sector(void)
419 {
420 while (cd.offset != 0)
421 write_byte(0);
422 }
423
424 /*-----------------------------------------------------------------------------
425 This function writes a string to the CD-ROM image. The terminating \0 is not
426 written.
427 -----------------------------------------------------------------------------*/
428
429 static void write_string(char *s)
430 {
431 while (*s != 0)
432 write_byte(*s++);
433 }
434
435 static void write_bytecounted_string(unsigned bytecount, char *s, char padding)
436 {
437 while (*s != 0 && bytecount != 0)
438 {
439 write_byte(*s++);
440 bytecount--;
441 }
442 while (bytecount != 0)
443 {
444 write_byte(padding);
445 bytecount--;
446 }
447 }
448
449 /*-----------------------------------------------------------------------------
450 This function writes a ansi string as a big endian unicode string to the CD-ROM
451 image. The terminating \0 is not written.
452 -----------------------------------------------------------------------------*/
453
454 static void write_string_as_big_endian_unicode(char *s)
455 {
456 while (*s != 0)
457 {
458 write_big_endian_word(*s++);
459 }
460 }
461
462 static void write_bytecounted_string_as_big_endian_unicode(unsigned bytecount, char *s, char padding)
463 {
464 unsigned wordcount = bytecount / 2;
465
466 while (*s != 0 && wordcount != 0)
467 {
468 write_big_endian_word(*s++);
469 wordcount--;
470 }
471 while (wordcount != 0)
472 {
473 write_big_endian_word(padding);
474 wordcount--;
475 }
476
477 if (bytecount % 2 != 0)
478 write_byte(padding);
479 }
480
481 /*-----------------------------------------------------------------------------
482 This function writes a block of identical bytes to the CD-ROM image.
483 -----------------------------------------------------------------------------*/
484
485 static void write_block(unsigned count, BYTE value)
486 {
487 while (count != 0)
488 {
489 write_byte(value);
490 count--;
491 }
492 }
493
494 /*-----------------------------------------------------------------------------
495 This function writes a block of identical big endian words to the CD-ROM image.
496 -----------------------------------------------------------------------------*/
497
498 static void write_word_block(unsigned count, WORD value)
499 {
500 while (count != 0)
501 {
502 write_big_endian_word(value);
503 count--;
504 }
505 }
506
507 /*-----------------------------------------------------------------------------
508 This function writes a directory record to the CD_ROM image.
509 -----------------------------------------------------------------------------*/
510
511 static void
512 write_directory_record(PDIR_RECORD d,
513 DIR_RECORD_TYPE DirType,
514 BOOL joliet)
515 {
516 unsigned identifier_size;
517 unsigned record_size;
518
519 if (joliet)
520 {
521 if (DirType == DOT_RECORD || DirType == DOT_DOT_RECORD)
522 identifier_size = 1;
523 else
524 identifier_size = strlen(d->joliet_name) * 2;
525 }
526 else
527 {
528 switch (DirType)
529 {
530 case DOT_RECORD:
531 case DOT_DOT_RECORD:
532 identifier_size = 1;
533 break;
534 case SUBDIRECTORY_RECORD:
535 /*printf("Subdir: %s\n", d->name_on_cd);*/
536 identifier_size = strlen(d->name_on_cd);
537 break;
538 case FILE_RECORD:
539 /*printf("File: %s.%s -> %s.%s\n", d->name, d->extension, d->name_on_cd, d->extension_on_cd);*/
540 identifier_size = strlen(d->name_on_cd) + 2;
541 if (d->extension_on_cd[0] != 0)
542 identifier_size += 1 + strlen(d->extension_on_cd);
543 break;
544 default:
545 identifier_size = 1;
546 break;
547 }
548 }
549 record_size = 33 + identifier_size;
550 if ((identifier_size & 1) == 0)
551 record_size++;
552 if (cd.offset + record_size > SECTOR_SIZE)
553 fill_sector();
554 write_byte((BYTE)record_size);
555 write_byte(0); // number of sectors in extended attribute record
556 if (joliet)
557 {
558 write_both_endian_dword(d->joliet_sector);
559 write_both_endian_dword(d->joliet_size);
560 }
561 else
562 {
563 write_both_endian_dword(d->sector);
564 write_both_endian_dword(d->size);
565 }
566 write_byte((BYTE)(d->date_and_time.year - 1900));
567 write_byte(d->date_and_time.month);
568 write_byte(d->date_and_time.day);
569 write_byte(d->date_and_time.hour);
570 write_byte(d->date_and_time.minute);
571 write_byte(d->date_and_time.second);
572 write_byte(0); // GMT offset
573 write_byte(d->flags);
574 write_byte(0); // file unit size for an interleaved file
575 write_byte(0); // interleave gap size for an interleaved file
576 write_both_endian_word(1); // volume sequence number
577 write_byte((BYTE)identifier_size);
578 switch (DirType)
579 {
580 case DOT_RECORD:
581 write_byte(0);
582 break;
583 case DOT_DOT_RECORD:
584 write_byte(1);
585 break;
586 case SUBDIRECTORY_RECORD:
587 if (joliet)
588 write_string_as_big_endian_unicode(d->joliet_name);
589 else
590 write_string(d->name_on_cd);
591 break;
592 case FILE_RECORD:
593 if (joliet)
594 {
595 write_string_as_big_endian_unicode(d->joliet_name);
596 }
597 else
598 {
599 write_string(d->name_on_cd);
600 if (d->extension_on_cd[0] != 0)
601 {
602 write_byte('.');
603 write_string(d->extension_on_cd);
604 }
605 write_string(";1");
606 }
607 break;
608 }
609 if ((identifier_size & 1) == 0)
610 write_byte(0);
611 }
612
613 /*-----------------------------------------------------------------------------
614 This function converts the date and time words from an ffblk structure and
615 puts them into a date_and_time structure.
616 -----------------------------------------------------------------------------*/
617
618 static void convert_date_and_time(PDATE_AND_TIME dt, time_t *time)
619 {
620 struct tm *timedef;
621 timedef = gmtime(time);
622
623 dt->second = timedef->tm_sec;
624 dt->minute = timedef->tm_min;
625 dt->hour = timedef->tm_hour;
626 dt->day = timedef->tm_mday;
627 dt->month = timedef->tm_mon + 1;
628 dt->year = timedef->tm_year + 1900;
629 }
630
631 /*-----------------------------------------------------------------------------
632 This function checks the specified character, if necessary, and
633 generates an error if it is a punctuation mark other than an underscore.
634 It also converts small letters to capital letters and returns the
635 result.
636 -----------------------------------------------------------------------------*/
637
638 static int check_for_punctuation(int c, const char *name)
639 {
640 c = toupper(c & 0xFF);
641 if (!accept_punctuation_marks && !isalnum(c) && c != '_')
642 error_exit("Punctuation mark in %s", name);
643 return c;
644 }
645
646 /*-----------------------------------------------------------------------------
647 This function checks to see if there's a cdname conflict.
648 -----------------------------------------------------------------------------*/
649
650 #if defined(_WIN32) && !defined(strcasecmp)
651 #define strcasecmp stricmp
652 #endif // _WIN32
653
654 static BOOL cdname_exists(PDIR_RECORD d)
655 {
656 PDIR_RECORD p = d->parent->first_record;
657 while (p)
658 {
659 if ( p != d
660 && !strcasecmp(p->name_on_cd, d->name_on_cd)
661 && !strcasecmp(p->extension_on_cd, d->extension_on_cd) )
662 return TRUE;
663 p = p->next_in_directory;
664 }
665 return FALSE;
666 }
667
668 static void parse_filename_into_dirrecord(const char* filename, PDIR_RECORD d, BOOL dir)
669 {
670 const char *s = filename;
671 char *t = d->name_on_cd;
672 char *n = d->name;
673 int joliet_length;
674 int filename_counter;
675 filename_counter = 1;
676 while (*s != 0)
677 {
678 if (*s == '.')
679 {
680 s++;
681 break;
682 }
683
684 if ((size_t)(t-d->name_on_cd) < sizeof(d->name_on_cd)-1)
685 *t++ = check_for_punctuation(*s, filename);
686 else if (!joliet)
687 error_exit("'%s' is not ISO-9660, aborting...", filename);
688
689 if ((size_t)(n-d->name) < sizeof(d->name)-1)
690 *n++ = *s;
691 else if (!joliet)
692 error_exit("'%s' is not ISO-9660, aborting...", filename);
693 s++;
694 }
695 // Check for extension length
696 if (!joliet && strlen(s) > MAX_EXTENSION_LENGTH)
697 {
698 error_exit("'%s' has too long extension, aborting...", filename);
699 }
700 *t = 0;
701 strcpy(d->extension, s);
702 t = d->extension_on_cd;
703 while (*s != 0)
704 {
705 if ((size_t)(t-d->extension_on_cd) < sizeof(d->extension_on_cd)-1)
706 *t++ = check_for_punctuation(*s, filename);
707 else if (!joliet)
708 error_exit("'%s' is not ISO-9660, aborting...", filename);
709 s++;
710 }
711 *t = 0;
712 *n = 0;
713
714 if (dir)
715 {
716 if (d->extension[0] != 0)
717 {
718 if (!joliet)
719 error_exit("Directory with extension '%s'", filename);
720 }
721 d->flags = DIRECTORY_FLAG;
722 } else
723 {
724 d->flags = 0;
725 }
726
727 filename_counter = 1;
728 while (cdname_exists(d))
729 {
730 // the file name must be at least 8 chars long
731 if (strlen(d->name_on_cd)<8)
732 error_exit("'%s' is a duplicate file name, aborting...", filename);
733
734 if ((d->name_on_cd[8] == '.') && (strlen(d->name_on_cd) < 13))
735 error_exit("'%s' is a duplicate file name, aborting...", filename);
736
737 // max 255 times for equal short filename
738 if (filename_counter>255)
739 error_exit("'%s' is a duplicate file name, aborting...", filename);
740
741 d->name_on_cd[8] = '~';
742 memset(&d->name_on_cd[9],0,5);
743 sprintf(&d->name_on_cd[9],"%d",filename_counter);
744 filename_counter++;
745 }
746
747 if (joliet)
748 {
749 joliet_length = strlen(filename);
750 if (joliet_length > 64)
751 error_exit("'%s' is not Joliet, aborting...", filename);
752 d->joliet_name = malloc(joliet_length + 1);
753 if (d->joliet_name == NULL)
754 error_exit("Insufficient memory");
755 strcpy(d->joliet_name, filename);
756 }
757 }
758
759 /*-----------------------------------------------------------------------------
760 This function creates a new directory record with the information from the
761 specified ffblk. It links it into the beginning of the directory list
762 for the specified parent and returns a pointer to the new record.
763 -----------------------------------------------------------------------------*/
764
765 #ifdef _WIN32
766
767 /* Win32 version */
768 PDIR_RECORD
769 new_directory_record(struct _finddata_t *f,
770 PDIR_RECORD parent)
771 {
772 PDIR_RECORD d;
773
774 d = calloc(1, sizeof(*d));
775 if (d == NULL)
776 error_exit("Insufficient memory");
777 d->next_in_memory = root.next_in_memory;
778 root.next_in_memory = d;
779
780 /* I need the parent set before calling parse_filename_into_dirrecord(),
781 because that functions checks for duplicate file names*/
782 d->parent = parent;
783 parse_filename_into_dirrecord(f->name, d, f->attrib & _A_SUBDIR);
784
785 convert_date_and_time(&d->date_and_time, &f->time_write);
786 d->flags |= f->attrib & _A_HIDDEN ? HIDDEN_FLAG : 0;
787 d->size = d->joliet_size = f->size;
788 d->next_in_directory = parent->first_record;
789 parent->first_record = d;
790 return d;
791 }
792
793 #else
794
795 /* Linux version */
796 PDIR_RECORD
797 new_directory_record(struct dirent *entry,
798 struct stat *stbuf,
799 PDIR_RECORD parent)
800 {
801 PDIR_RECORD d;
802
803 d = calloc(1, sizeof(*d));
804 if (d == NULL)
805 error_exit("Insufficient memory");
806 d->next_in_memory = root.next_in_memory;
807 root.next_in_memory = d;
808
809 /* I need the parent set before calling parse_filename_into_dirrecord(),
810 because that functions checks for duplicate file names*/
811 d->parent = parent;
812 #ifdef HAVE_D_TYPE
813 parse_filename_into_dirrecord(entry->d_name, d, entry->d_type == DT_DIR);
814 #else
815 parse_filename_into_dirrecord(entry->d_name, d, S_ISDIR(stbuf->st_mode));
816 #endif
817
818 convert_date_and_time(&d->date_and_time, &stbuf->st_mtime);
819 d->flags |= entry->d_name[0] == '.' ? HIDDEN_FLAG : 0;
820 d->size = d->joliet_size = stbuf->st_size;
821 d->next_in_directory = parent->first_record;
822 parent->first_record = d;
823 return d;
824 }
825
826 #endif
827
828 /*-----------------------------------------------------------------------------
829 This function compares two directory records according to the ISO9660 rules
830 for directory sorting and returns a negative value if p is before q, or a
831 positive value if p is after q.
832 -----------------------------------------------------------------------------*/
833
834 static int compare_directory_order(PDIR_RECORD p, PDIR_RECORD q)
835 {
836 int n = strcmp(p->name_on_cd, q->name_on_cd);
837 if (n == 0)
838 n = strcmp(p->extension_on_cd, q->extension_on_cd);
839 return n;
840 }
841
842 /*-----------------------------------------------------------------------------
843 This function compares two directory records (which must represent
844 directories) according to the ISO9660 rules for path table sorting and returns
845 a negative value if p is before q, or a positive vlaue if p is after q.
846 -----------------------------------------------------------------------------*/
847
848 static int compare_path_table_order(PDIR_RECORD p, PDIR_RECORD q)
849 {
850 int n = p->level - q->level;
851 if (p == q)
852 return 0;
853 if (n == 0)
854 {
855 n = compare_path_table_order(p->parent, q->parent);
856 if (n == 0)
857 n = compare_directory_order(p, q);
858 }
859 return n;
860 }
861
862 /*-----------------------------------------------------------------------------
863 This function appends the specified string to the buffer source[].
864 -----------------------------------------------------------------------------*/
865
866 static void append_string_to_source(char *s)
867 {
868 while (*s != 0)
869 *end_source++ = *s++;
870 }
871
872 /*-----------------------------------------------------------------------------
873 This function scans all files from the current source[] (which must end in \,
874 and represents a directory already in the database as d),
875 and puts the appropriate directory records into the database in memory, with
876 the specified root. It calls itself recursively to scan all subdirectories.
877 -----------------------------------------------------------------------------*/
878
879 #ifdef _WIN32
880
881 static void
882 make_directory_records(PDIR_RECORD d)
883 {
884 PDIR_RECORD new_d;
885 struct _finddata_t f;
886 char *old_end_source;
887 int findhandle;
888
889 d->first_record = NULL;
890 strcpy(end_source, "*.*");
891
892 findhandle = _findfirst(source, &f);
893 if (findhandle != 0)
894 {
895 do
896 {
897 if ((f.attrib & (_A_HIDDEN | _A_SUBDIR)) == 0 && f.name[0] != '.')
898 {
899 if (strcmp(f.name, DIRECTORY_TIMESTAMP) == 0)
900 {
901 convert_date_and_time(&d->date_and_time, &f.time_write);
902 }
903 else
904 {
905 if (verbosity == VERBOSE)
906 {
907 old_end_source = end_source;
908 strcpy(end_source, f.name);
909 printf("%d: file %s\n", d->level, source);
910 end_source = old_end_source;
911 }
912 (void) new_directory_record(&f, d);
913 }
914 }
915 }
916 while (_findnext(findhandle, &f) == 0);
917
918 _findclose(findhandle);
919 }
920
921 strcpy(end_source, "*.*");
922 findhandle = _findfirst(source, &f);
923 if (findhandle)
924 {
925 do
926 {
927 if (f.attrib & _A_SUBDIR && f.name[0] != '.')
928 {
929 old_end_source = end_source;
930 append_string_to_source(f.name);
931 *end_source++ = DIR_SEPARATOR_CHAR;
932 if (verbosity == VERBOSE)
933 {
934 *end_source = 0;
935 printf("%d: directory %s\n", d->level + 1, source);
936 }
937 if (d->level < MAX_LEVEL)
938 {
939 new_d = new_directory_record(&f, d);
940 new_d->next_in_path_table = root.next_in_path_table;
941 root.next_in_path_table = new_d;
942 new_d->level = d->level + 1;
943 make_directory_records(new_d);
944 }
945 else
946 {
947 error_exit("Directory is nested too deep");
948 }
949 end_source = old_end_source;
950 }
951 }
952 while (_findnext(findhandle, &f) == 0);
953
954 _findclose(findhandle);
955 }
956
957 // sort directory
958 d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order);
959 }
960
961 #else
962
963 /* Linux version */
964 static void
965 make_directory_records(PDIR_RECORD d)
966 {
967 PDIR_RECORD new_d;
968 DIR *dirp;
969 struct dirent *entry;
970 char *old_end_source;
971 struct stat stbuf;
972 char buf[MAX_PATH];
973
974 d->first_record = NULL;
975
976 #ifdef HAVE_D_TYPE
977 dirp = opendir(source);
978 if (dirp != NULL)
979 {
980 while ((entry = readdir(dirp)) != NULL)
981 {
982 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
983 continue; // skip self and parent
984
985 if (entry->d_type == DT_REG) // normal file
986 {
987 // Check for an absolute path
988 if (source[0] == DIR_SEPARATOR_CHAR)
989 {
990 strcpy(buf, source);
991 strcat(buf, DIR_SEPARATOR_STRING);
992 strcat(buf, entry->d_name);
993 }
994 else
995 {
996 if (!getcwd(buf, sizeof(buf)))
997 error_exit("Cannot get CWD: %s\n", strerror(errno));
998 strcat(buf, DIR_SEPARATOR_STRING);
999 strcat(buf, source);
1000 strcat(buf, entry->d_name);
1001 }
1002
1003 if (stat(buf, &stbuf) == -1)
1004 {
1005 error_exit("Cannot access '%s' (%s)\n", buf, strerror(errno));
1006 return;
1007 }
1008
1009 if (strcmp(entry->d_name, DIRECTORY_TIMESTAMP) == 0)
1010 {
1011 convert_date_and_time(&d->date_and_time, &stbuf.st_ctime);
1012 }
1013 else
1014 {
1015 if (verbosity == VERBOSE)
1016 {
1017 printf("%d: file %s\n", d->level, buf);
1018 }
1019 (void) new_directory_record(entry, &stbuf, d);
1020 }
1021 }
1022 }
1023 closedir(dirp);
1024 }
1025 else
1026 {
1027 error_exit("Cannot open '%s'\n", source);
1028 return;
1029 }
1030
1031 dirp = opendir(source);
1032 if (dirp != NULL)
1033 {
1034 while ((entry = readdir(dirp)) != NULL)
1035 {
1036 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
1037 continue; // skip self and parent
1038
1039 if (entry->d_type == DT_DIR) // directory
1040 {
1041 old_end_source = end_source;
1042 append_string_to_source(entry->d_name);
1043 *end_source++ = DIR_SEPARATOR_CHAR;
1044 *end_source = 0;
1045 if (verbosity == VERBOSE)
1046 {
1047 printf("%d: directory %s\n", d->level + 1, source);
1048 }
1049 if (d->level < MAX_LEVEL)
1050 {
1051 // Check for an absolute path
1052 if (source[0] == DIR_SEPARATOR_CHAR)
1053 {
1054 strcpy(buf, source);
1055 }
1056 else
1057 {
1058 if (!getcwd(buf, sizeof(buf)))
1059 error_exit("Cannot get CWD: %s\n", strerror(errno));
1060 strcat(buf, DIR_SEPARATOR_STRING);
1061 strcat(buf, source);
1062 }
1063
1064 if (stat(buf, &stbuf) == -1)
1065 {
1066 error_exit("Cannot access '%s' (%s)\n", buf, strerror(errno));
1067 return;
1068 }
1069 new_d = new_directory_record(entry, &stbuf, d);
1070 new_d->next_in_path_table = root.next_in_path_table;
1071 root.next_in_path_table = new_d;
1072 new_d->level = d->level + 1;
1073 make_directory_records(new_d);
1074 }
1075 else
1076 {
1077 error_exit("Directory is nested too deep");
1078 }
1079 end_source = old_end_source;
1080 *end_source = 0;
1081 }
1082 }
1083 closedir(dirp);
1084 }
1085 else
1086 {
1087 error_exit("Cannot open '%s'\n", source);
1088 return;
1089 }
1090
1091 #else
1092
1093 dirp = opendir(source);
1094 if (dirp != NULL)
1095 {
1096 while ((entry = readdir(dirp)) != NULL)
1097 {
1098 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
1099 continue; // skip self and parent
1100
1101 // Check for an absolute path
1102 if (source[0] == DIR_SEPARATOR_CHAR)
1103 {
1104 strcpy(buf, source);
1105 strcat(buf, DIR_SEPARATOR_STRING);
1106 strcat(buf, entry->d_name);
1107 }
1108 else
1109 {
1110 if (!getcwd(buf, sizeof(buf)))
1111 error_exit("Cannot get CWD: %s\n", strerror(errno));
1112 strcat(buf, DIR_SEPARATOR_STRING);
1113 strcat(buf, source);
1114 strcat(buf, entry->d_name);
1115 }
1116
1117 if (stat(buf, &stbuf) == -1)
1118 {
1119 error_exit("Cannot access '%s' (%s)\n", buf, strerror(errno));
1120 return;
1121 }
1122
1123 if (S_ISDIR(stbuf.st_mode))
1124 {
1125 old_end_source = end_source;
1126 append_string_to_source(entry->d_name);
1127 *end_source++ = DIR_SEPARATOR_CHAR;
1128 *end_source = 0;
1129 if (verbosity == VERBOSE)
1130 {
1131 printf("%d: directory %s\n", d->level + 1, source);
1132 }
1133
1134 if (d->level < MAX_LEVEL)
1135 {
1136 new_d = new_directory_record(entry, &stbuf, d);
1137 new_d->next_in_path_table = root.next_in_path_table;
1138 root.next_in_path_table = new_d;
1139 new_d->level = d->level + 1;
1140 make_directory_records(new_d);
1141 }
1142 else
1143 {
1144 error_exit("Directory is nested too deep");
1145 }
1146
1147 end_source = old_end_source;
1148 *end_source = 0;
1149 }
1150 else if (S_ISREG(stbuf.st_mode))
1151 {
1152 if (strcmp(entry->d_name, DIRECTORY_TIMESTAMP) == 0)
1153 {
1154 convert_date_and_time(&d->date_and_time, &stbuf.st_ctime);
1155 }
1156 else
1157 {
1158 if (verbosity == VERBOSE)
1159 {
1160 printf("%d: file %s\n", d->level, buf);
1161 }
1162 (void) new_directory_record(entry, &stbuf, d);
1163 }
1164 }
1165 }
1166 closedir(dirp);
1167 }
1168 else
1169 {
1170 error_exit("Cannot open '%s'\n", source);
1171 return;
1172 }
1173
1174 #endif
1175
1176 // sort directory
1177 d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order);
1178 }
1179
1180 #endif
1181
1182 static PDIR_RECORD
1183 new_empty_dirrecord(PDIR_RECORD d, BOOL directory)
1184 {
1185 PDIR_RECORD new_d;
1186 new_d = calloc(1, sizeof(*new_d));
1187 new_d->parent = d;
1188 new_d->level = d->level + 1;
1189 new_d->next_in_directory = d->first_record;
1190 d->first_record = new_d;
1191 new_d->next_in_memory = root.next_in_memory;
1192 root.next_in_memory = new_d;
1193 new_d->date_and_time = d->date_and_time;
1194 if (directory)
1195 {
1196 new_d->flags |= DIRECTORY_FLAG;
1197 new_d->next_in_path_table = root.next_in_path_table;
1198 root.next_in_path_table = new_d;
1199 }
1200 return new_d;
1201 }
1202
1203 #ifdef _WIN32
1204 static BOOL
1205 get_cd_file_time(HANDLE handle, PDATE_AND_TIME cd_time_info)
1206 {
1207 FILETIME file_time;
1208 SYSTEMTIME sys_time;
1209
1210 if (!GetFileTime(handle, NULL, NULL, &file_time))
1211 return FALSE;
1212
1213 FileTimeToSystemTime(&file_time, &sys_time);
1214 memset(cd_time_info, 0, sizeof(*cd_time_info));
1215
1216 cd_time_info->year = sys_time.wYear;
1217 cd_time_info->month = sys_time.wMonth;
1218 cd_time_info->day = sys_time.wDay;
1219 cd_time_info->hour = sys_time.wHour;
1220 cd_time_info->minute = sys_time.wMinute;
1221 cd_time_info->second = sys_time.wSecond;
1222
1223 return TRUE;
1224 }
1225 #endif
1226
1227 static void
1228 scan_specified_files(PDIR_RECORD d, struct target_dir_entry *dir)
1229 {
1230 PDIR_RECORD new_d;
1231 #ifdef _WIN32
1232 HANDLE open_file;
1233 LARGE_INTEGER file_size;
1234 #else
1235 struct stat stbuf;
1236 #endif
1237 struct target_file *file;
1238 struct target_dir_entry *child;
1239
1240 d->first_record = NULL;
1241
1242 for (file = dir->head; file; file = file->next)
1243 {
1244 if (strcmp(file->target_name, DIRECTORY_TIMESTAMP) == 0)
1245 {
1246 #ifdef _WIN32
1247 if ((open_file = CreateFileA(file->source_name,
1248 GENERIC_READ,
1249 FILE_SHARE_READ,
1250 NULL,
1251 OPEN_EXISTING,
1252 FILE_ATTRIBUTE_NORMAL,
1253 NULL)) == INVALID_HANDLE_VALUE)
1254 {
1255 error_exit("Cannot open timestamp file '%s'\n", file->source_name);
1256 }
1257
1258 if (!get_cd_file_time(open_file, &d->date_and_time))
1259 {
1260 error_exit("Cannot stat timestamp file '%s'\n", file->source_name);
1261 }
1262 CloseHandle(open_file);
1263 #else
1264 if (stat(file->target_name, &stbuf) == -1)
1265 {
1266 error_exit("Cannot stat timestamp file '%s'\n", file->source_name);
1267 }
1268 convert_date_and_time(&d->date_and_time, &stbuf.st_ctime);
1269 #endif
1270 }
1271 else
1272 {
1273 if (verbosity == VERBOSE)
1274 {
1275 printf("%d: file %s (from %s)\n",
1276 d->level,
1277 file->target_name,
1278 file->source_name);
1279 }
1280 new_d = new_empty_dirrecord(d, FALSE);
1281 parse_filename_into_dirrecord(file->target_name, new_d, FALSE);
1282 #ifdef _WIN32
1283 if ((open_file = CreateFileA(file->source_name,
1284 GENERIC_READ,
1285 FILE_SHARE_READ,
1286 NULL,
1287 OPEN_EXISTING,
1288 FILE_ATTRIBUTE_NORMAL,
1289 NULL)) == INVALID_HANDLE_VALUE)
1290 {
1291 error_exit("Cannot open file '%s'\n", file->source_name);
1292 }
1293 if (!get_cd_file_time(open_file, &new_d->date_and_time))
1294 {
1295 error_exit("Cannot stat file '%s'\n", file->source_name);
1296 }
1297 if (!GetFileSizeEx(open_file, &file_size))
1298 {
1299 error_exit("Cannot get file size of '%s'\n", file->source_name);
1300 }
1301 new_d->size = new_d->joliet_size = file_size.QuadPart;
1302 new_d->orig_name = file->source_name;
1303 CloseHandle(open_file);
1304 #else
1305 if (stat(file->source_name, &stbuf) == -1)
1306 {
1307 error_exit("Cannot find '%s' (target '%s')\n",
1308 file->source_name,
1309 file->target_name);
1310 }
1311 convert_date_and_time(&new_d->date_and_time, &stbuf.st_mtime);
1312 new_d->size = new_d->joliet_size = stbuf.st_size;
1313 new_d->orig_name = file->source_name;
1314 #endif
1315 }
1316 }
1317
1318 for (child = dir->child; child; child = child->next)
1319 {
1320 if (verbosity == VERBOSE)
1321 {
1322 printf("%d: directory %s\n", d->level, child->case_name);
1323 }
1324 new_d = new_empty_dirrecord(d, TRUE);
1325 parse_filename_into_dirrecord(child->case_name, new_d, TRUE);
1326 scan_specified_files(new_d, child);
1327 }
1328
1329 /* sort directory */
1330 d->first_record = sort_linked_list(d->first_record,
1331 0,
1332 compare_directory_order);
1333 source[0] = 0;
1334 end_source = source;
1335 }
1336
1337 /*-----------------------------------------------------------------------------
1338 This function loads the file specifications for the file or directory
1339 corresponding to the specified directory record into the source[] buffer. It
1340 is recursive.
1341 -----------------------------------------------------------------------------*/
1342
1343 static void get_file_specifications(PDIR_RECORD d)
1344 {
1345 if (d != &root)
1346 {
1347 get_file_specifications(d->parent);
1348 if (d->joliet_name == NULL)
1349 append_string_to_source(d->name);
1350 else
1351 append_string_to_source(d->joliet_name);
1352
1353 if (((d->flags & DIRECTORY_FLAG) == 0 || joliet) && d->extension[0] != 0)
1354 {
1355 if (d->joliet_name == NULL)
1356 {
1357 *end_source++ = '.';
1358 append_string_to_source(d->extension);
1359 }
1360 }
1361 if (d->flags & DIRECTORY_FLAG)
1362 *end_source++ = DIR_SEPARATOR_CHAR;
1363 }
1364 }
1365
1366 static void get_time_string(char *str)
1367 {
1368 sprintf(str, "%04d%02d%02d%02d%02d%02d00",
1369 root.date_and_time.year,
1370 root.date_and_time.month,
1371 root.date_and_time.day,
1372 root.date_and_time.hour,
1373 root.date_and_time.minute,
1374 root.date_and_time.second);
1375 }
1376
1377 static BOOL write_from_file(FILE *file, DWORD size)
1378 {
1379 if (cd.file != NULL)
1380 {
1381 int n;
1382
1383 fseek(file, 0, SEEK_SET);
1384 while (size > 0)
1385 {
1386 n = BUFFER_SIZE - cd.count;
1387 if ((DWORD)n > size)
1388 n = size;
1389
1390 if (fread(cd.buffer + cd.count, n, 1, file) < 1)
1391 return FALSE;
1392
1393 cd.count += n;
1394 if (cd.count == BUFFER_SIZE)
1395 flush_buffer();
1396 cd.sector += n / SECTOR_SIZE;
1397 cd.offset += n % SECTOR_SIZE;
1398 size -= n;
1399 }
1400 }
1401 else
1402 {
1403 cd.sector += size / SECTOR_SIZE;
1404 cd.offset += size % SECTOR_SIZE;
1405 }
1406
1407 return TRUE;
1408 }
1409
1410 static void pass(void)
1411 {
1412 PDIR_RECORD d, q;
1413 unsigned int index;
1414 unsigned int name_length;
1415 DWORD size;
1416 DWORD number_of_sectors;
1417 char *old_end_source;
1418 FILE *file;
1419
1420 PBOOT_HEADER header;
1421 PBOOT_ENTRY entry;
1422
1423 char timestring[17];
1424
1425 get_time_string(timestring);
1426
1427 // first 16 sectors are zeros
1428 write_block(16 * SECTOR_SIZE, 0);
1429
1430
1431 // Primary Volume Descriptor
1432 if (make_bridged_udf)
1433 {
1434 write_string("\1CD001\1");
1435 write_byte(0);
1436 write_bytecounted_string(32, "", ' '); // system identifier
1437 write_bytecounted_string(32, volume_label, ' '); // volume label
1438
1439 write_block(8, 0);
1440 write_both_endian_dword(total_sectors);
1441 write_block(32, 0);
1442 write_both_endian_word(1); // volume set size
1443 write_both_endian_word(1); // volume sequence number
1444 write_both_endian_word(2048); // sector size
1445 write_both_endian_dword(path_table_size);
1446 write_little_endian_dword(little_endian_path_table_sector);
1447 write_little_endian_dword(0); // second little endian path table
1448 write_big_endian_dword(big_endian_path_table_sector);
1449 write_big_endian_dword(0); // second big endian path table
1450 write_directory_record(&root, DOT_RECORD, FALSE);
1451
1452 write_bytecounted_string(128, volume_label, ' '); // volume set identifier
1453 write_bytecounted_string(128, PUBLISHER_ID, ' '); // publisher identifier
1454 write_bytecounted_string(128, DATA_PREP_ID, ' '); // data preparer identifier
1455 write_bytecounted_string(128, APP_ID, ' '); // application identifier
1456
1457 write_bytecounted_string(37, "", ' '); // copyright file identifier
1458 write_bytecounted_string(37, "", ' '); // abstract file identifier
1459 write_bytecounted_string(37, "", ' '); // bibliographic file identifier
1460
1461 write_string(timestring); // volume creation
1462 write_byte(0);
1463 write_string(timestring); // most recent modification
1464 write_byte(0);
1465 write_string("0000000000000000"); // volume expires
1466 write_byte(0);
1467 write_string("0000000000000000"); // volume is effective
1468 write_byte(0);
1469 write_byte(1);
1470 write_byte(0);
1471 fill_sector();
1472 }
1473
1474 // Boot Volume Descriptor
1475 if (eltorito)
1476 {
1477 write_byte(0); // Boot record ID
1478 write_string("CD001\1");
1479 write_bytecounted_string(32, "EL TORITO SPECIFICATION", 0); // El-Torito identifier
1480 write_block(32, 0); // unused
1481 write_little_endian_dword(boot_catalog_sector); // pointer to boot catalog
1482 fill_sector();
1483 }
1484
1485 // Supplementary Volume Descriptor
1486 if (joliet)
1487 {
1488 write_string("\2CD001\1");
1489 write_byte(0);
1490 write_bytecounted_string_as_big_endian_unicode(32, "", ' '); // system identifier
1491 write_bytecounted_string_as_big_endian_unicode(32, volume_label, ' '); // volume label
1492
1493 write_block(8, 0);
1494 write_both_endian_dword(total_sectors);
1495 write_string("%/E");
1496 write_block(29, 0);
1497 write_both_endian_word(1); // volume set size
1498 write_both_endian_word(1); // volume sequence number
1499 write_both_endian_word(2048); // sector size
1500 write_both_endian_dword(joliet_path_table_size);
1501 write_little_endian_dword(joliet_little_endian_path_table_sector);
1502 write_little_endian_dword(0); // second little endian path table
1503 write_big_endian_dword(joliet_big_endian_path_table_sector);
1504 write_big_endian_dword(0); // second big endian path table
1505 write_directory_record(&root, DOT_RECORD, TRUE);
1506
1507 write_bytecounted_string_as_big_endian_unicode(128, volume_label, ' '); // volume set identifier
1508 write_bytecounted_string_as_big_endian_unicode(128, PUBLISHER_ID, ' '); // publisher identifier
1509 write_bytecounted_string_as_big_endian_unicode(128, DATA_PREP_ID, ' '); // data preparer identifier
1510 write_bytecounted_string_as_big_endian_unicode(128, APP_ID, ' '); // application identifier
1511
1512 write_bytecounted_string_as_big_endian_unicode(37, "", ' '); // copyright file identifier
1513 write_bytecounted_string_as_big_endian_unicode(37, "", ' '); // abstract file identifier
1514 write_bytecounted_string_as_big_endian_unicode(37, "", ' '); // bibliographic file identifier
1515
1516 write_string(timestring); // volume creation
1517 write_byte(0);
1518 write_string(timestring); // most recent modification
1519 write_byte(0);
1520 write_string("0000000000000000"); // volume expires
1521 write_byte(0);
1522 write_string("0000000000000000"); // volume is effective
1523 write_byte(0);
1524 write_byte(1);
1525 write_byte(0);
1526 fill_sector();
1527 }
1528
1529 // Volume Descriptor Set Terminator
1530 if (make_bridged_udf)
1531 {
1532 write_string("\377CD001\1");
1533 fill_sector();
1534 }
1535
1536 // TODO: Add UDF support!
1537
1538 // Boot Catalog
1539 if (eltorito)
1540 {
1541 boot_catalog_sector = cd.sector;
1542
1543 // Validation entry header
1544 write_byte(boot_validation_header.header_id);
1545 write_byte(boot_validation_header.platform_id);
1546 write_little_endian_word(0); // reserved
1547 write_bytecounted_string(24, MANUFACTURER_ID, 0); // Manufacturer identifier
1548 write_little_endian_word(0x62E); // checksum // FIXME: This is hardcoded!!
1549 write_little_endian_word(0xAA55); // signature
1550
1551 // Default entry
1552 write_byte(default_boot_entry.boot_id);
1553 write_byte(default_boot_entry.boot_emu_type);
1554 write_little_endian_word(default_boot_entry.load_segment);
1555 write_byte(0); // partition type
1556 write_byte(0); // unused
1557 write_little_endian_word(default_boot_entry.sector_count);
1558 write_little_endian_dword(default_boot_entry.load_rba);
1559 write_block(20, 0); // unused
1560
1561 // Loop through each boot header
1562 header = boot_header_list;
1563 while (header)
1564 {
1565 write_byte(header->header_id);
1566 write_byte(header->platform_id);
1567 write_little_endian_word(header->num_entries);
1568 write_block(28, 0); // Identifier string (unused)
1569
1570 // Loop through each boot entry
1571 entry = header->entry_list;
1572 while (entry)
1573 {
1574 write_byte(entry->boot_id);
1575 write_byte(entry->boot_emu_type);
1576 write_little_endian_word(entry->load_segment);
1577 write_byte(0); // partition type
1578 write_byte(0); // unused
1579 write_little_endian_word(entry->sector_count);
1580 write_little_endian_dword(entry->load_rba);
1581 write_block(20, 0); // Selection criteria (unused)
1582
1583 entry = entry->next_entry;
1584 }
1585
1586 header = header->next_header;
1587 }
1588
1589 fill_sector();
1590 }
1591
1592
1593 // Boot Images
1594 if (eltorito)
1595 {
1596 default_boot_entry.load_rba = cd.sector;
1597
1598 file = fopen(default_boot_entry.bootimage, "rb");
1599 if (file == NULL)
1600 error_exit("Cannot open '%s'\n", default_boot_entry.bootimage);
1601 fseek(file, 0, SEEK_END);
1602 size = ftell(file);
1603 if (size == 0 || (size % 2048))
1604 {
1605 fclose(file);
1606 error_exit("Invalid boot image size (%lu bytes)\n", size);
1607 }
1608 // Sector count in 512 byte sectors and rounded up
1609 default_boot_entry.sector_count = (size + 511) / 512;
1610 if (!write_from_file(file, size))
1611 {
1612 fclose(file);
1613 error_exit("Read error in file '%s'\n", default_boot_entry.bootimage);
1614 }
1615 fclose(file);
1616
1617 // Loop through each boot header
1618 header = boot_header_list;
1619 while (header)
1620 {
1621 // Loop through each boot entry
1622 entry = header->entry_list;
1623 while (entry)
1624 {
1625 entry->load_rba = cd.sector;
1626
1627 file = fopen(entry->bootimage, "rb");
1628 if (file == NULL)
1629 error_exit("Cannot open '%s'\n", entry->bootimage);
1630 fseek(file, 0, SEEK_END);
1631 size = ftell(file);
1632 if (size == 0 || (size % 2048))
1633 {
1634 fclose(file);
1635 error_exit("Invalid boot image size (%lu bytes)\n", size);
1636 }
1637 // Sector count in 512 byte sectors and rounded up
1638 entry->sector_count = (size + 511) / 512;
1639 if (!write_from_file(file, size))
1640 {
1641 fclose(file);
1642 error_exit("Read error in file '%s'\n", entry->bootimage);
1643 }
1644 fclose(file);
1645
1646 entry = entry->next_entry;
1647 }
1648
1649 header = header->next_header;
1650 }
1651
1652 // fill_sector();
1653 }
1654
1655
1656 // Little Endian Path Table
1657
1658 little_endian_path_table_sector = cd.sector;
1659 write_byte(1);
1660 write_byte(0); // number of sectors in extended attribute record
1661 write_little_endian_dword(root.sector);
1662 write_little_endian_word(1);
1663 write_byte(0);
1664 write_byte(0);
1665
1666 index = 1;
1667 root.path_table_index = 1;
1668 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1669 {
1670 name_length = strlen(d->name_on_cd);
1671 write_byte((BYTE)name_length);
1672 write_byte(0); // number of sectors in extended attribute record
1673 write_little_endian_dword(d->sector);
1674 write_little_endian_word(d->parent->path_table_index);
1675 write_string(d->name_on_cd);
1676 if (name_length & 1)
1677 write_byte(0);
1678 d->path_table_index = ++index;
1679 }
1680
1681 path_table_size = (cd.sector - little_endian_path_table_sector) *
1682 SECTOR_SIZE + cd.offset;
1683 fill_sector();
1684
1685
1686 // Big Endian Path Table
1687
1688 big_endian_path_table_sector = cd.sector;
1689 write_byte(1);
1690 write_byte(0); // number of sectors in extended attribute record
1691 write_big_endian_dword(root.sector);
1692 write_big_endian_word(1);
1693 write_byte(0);
1694 write_byte(0);
1695
1696 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1697 {
1698 name_length = strlen(d->name_on_cd);
1699 write_byte((BYTE)name_length);
1700 write_byte(0); // number of sectors in extended attribute record
1701 write_big_endian_dword(d->sector);
1702 write_big_endian_word(d->parent->path_table_index);
1703 write_string(d->name_on_cd);
1704 if (name_length & 1)
1705 write_byte(0);
1706 }
1707 fill_sector();
1708
1709 if (joliet)
1710 {
1711 // Little Endian Path Table
1712
1713 joliet_little_endian_path_table_sector = cd.sector;
1714 write_byte(1);
1715 write_byte(0); // number of sectors in extended attribute record
1716 write_little_endian_dword(root.joliet_sector);
1717 write_little_endian_word(1);
1718 write_byte(0);
1719 write_byte(0);
1720
1721 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1722 {
1723 name_length = strlen(d->joliet_name) * 2;
1724 write_byte((BYTE)name_length);
1725 write_byte(0); // number of sectors in extended attribute record
1726 write_little_endian_dword(d->joliet_sector);
1727 write_little_endian_word(d->parent->path_table_index);
1728 write_string_as_big_endian_unicode(d->joliet_name);
1729 }
1730
1731 joliet_path_table_size = (cd.sector - joliet_little_endian_path_table_sector) *
1732 SECTOR_SIZE + cd.offset;
1733 fill_sector();
1734
1735 // Big Endian Path Table
1736
1737 joliet_big_endian_path_table_sector = cd.sector;
1738 write_byte(1);
1739 write_byte(0); // number of sectors in extended attribute record
1740 write_big_endian_dword(root.joliet_sector);
1741 write_big_endian_word(1);
1742 write_byte(0);
1743 write_byte(0);
1744
1745 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1746 {
1747 name_length = strlen(d->joliet_name) * 2;
1748 write_byte((BYTE)name_length);
1749 write_byte(0); // number of sectors in extended attribute record
1750 write_big_endian_dword(d->joliet_sector);
1751 write_big_endian_word(d->parent->path_table_index);
1752 write_string_as_big_endian_unicode(d->joliet_name);
1753 }
1754 fill_sector();
1755 }
1756
1757 // TODO: Add UDF support!
1758
1759 #if 0
1760 // Boot Images ??
1761 #endif
1762
1763 // TODO: Add CRC block for header!
1764
1765 // Directories and files
1766 for (d = &root; d != NULL; d = d->next_in_path_table)
1767 {
1768 // write directory
1769 d->sector = cd.sector;
1770 write_directory_record(d, DOT_RECORD, FALSE);
1771 write_directory_record(d == &root ? d : d->parent, DOT_DOT_RECORD, FALSE);
1772 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1773 {
1774 write_directory_record(q,
1775 q->flags & DIRECTORY_FLAG ? SUBDIRECTORY_RECORD : FILE_RECORD,
1776 FALSE);
1777 }
1778 fill_sector();
1779 d->size = (cd.sector - d->sector) * SECTOR_SIZE;
1780
1781 // write directory for joliet
1782 if (joliet)
1783 {
1784 d->joliet_sector = cd.sector;
1785 write_directory_record(d, DOT_RECORD, TRUE);
1786 write_directory_record(d == &root ? d : d->parent, DOT_DOT_RECORD, TRUE);
1787 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1788 {
1789 write_directory_record(q,
1790 q->flags & DIRECTORY_FLAG ? SUBDIRECTORY_RECORD : FILE_RECORD,
1791 TRUE);
1792 }
1793 fill_sector();
1794 d->joliet_size = (cd.sector - d->joliet_sector) * SECTOR_SIZE;
1795 bytes_in_directories += d->joliet_size;
1796 }
1797
1798 number_of_directories++;
1799 bytes_in_directories += d->size;
1800
1801 // write file data
1802 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1803 {
1804 if ((q->flags & DIRECTORY_FLAG) == 0)
1805 {
1806 q->sector = q->joliet_sector = cd.sector;
1807 size = q->size;
1808 if (cd.file == NULL)
1809 {
1810 number_of_sectors = (size + SECTOR_SIZE - 1) / SECTOR_SIZE;
1811 cd.sector += number_of_sectors;
1812 number_of_files++;
1813 bytes_in_files += size;
1814 unused_bytes_at_ends_of_files +=
1815 number_of_sectors * SECTOR_SIZE - size;
1816 }
1817 else
1818 {
1819 const char *file_source;
1820 old_end_source = end_source;
1821 if (!q->orig_name)
1822 {
1823 get_file_specifications(q);
1824 *end_source = 0;
1825 file_source = source;
1826 }
1827 else
1828 {
1829 file_source = q->orig_name;
1830 }
1831 if (verbosity == VERBOSE)
1832 printf("Writing contents of %s\n", file_source);
1833 file = fopen(file_source, "rb");
1834 if (file == NULL)
1835 error_exit("Cannot open '%s'\n", file_source);
1836 if (!write_from_file(file, size))
1837 {
1838 fclose(file);
1839 error_exit("Read error in file '%s'\n", file_source);
1840 }
1841 fclose(file);
1842 end_source = old_end_source;
1843 fill_sector();
1844 }
1845 }
1846 }
1847 }
1848
1849 // TODO: Add final CRC block!
1850
1851 total_sectors = (DWORD)cd.sector;
1852 }
1853
1854 static char HELP[] =
1855 "\n"
1856 "CDMAKE CD-ROM Premastering Utility\n"
1857 "Copyright (C) 1997 Philip J. Erdelsky\n"
1858 "Copyright (C) 2003-2016 ReactOS Team\n"
1859 "\n\n"
1860 "CDMAKE [-vN] [-p] [-s N] [-m] [-j] [-q] "
1861 // "[-x] "
1862 "[-pN] [-eN] [-b bootimage]\n"
1863 " [-bootdata:N#<defaultBootEntry>#<bootEntry1>#...#<bootEntryN>]\n"
1864 " source volume image\n"
1865 "\n"
1866 "Mandatory options:\n"
1867 " source Specifications of base directory containing all files to be\n"
1868 " written to CD-ROM image. Can be a directory, or a path to a\n"
1869 " file list (in which case the path must start with a '@').\n"
1870 " volume Volume label.\n"
1871 " image Image file or device.\n"
1872 "\n"
1873 "General options:\n"
1874 " -vN Verbosity level. Valid values for 'N' are:\n"
1875 " 0: Quiet mode - display nothing but error messages.\n"
1876 " 1: Normal mode (default).\n"
1877 " 2: Verbose mode - display file information as files are\n"
1878 " scanned and written. Overrides the -p option.\n"
1879 " -p Show progress while writing.\n"
1880 " -s N Abort operation before beginning write if image will be larger\n"
1881 " than N megabytes (i.e. 1024*1024*N bytes).\n"
1882 " -q Only scan the source files; do not create an image.\n"
1883 // " -x Compute and encode the AutoCRC value in the image.\n"
1884 "\n"
1885 "ISO 9660 and Joliet options:\n"
1886 " -m Accept punctuation marks other than underscores in names and\n"
1887 " extensions.\n"
1888 " -j Generate Joliet filename records.\n"
1889 "\n"
1890 // "UDF options:\n"
1891 // " Not implemented yet!\n"
1892 // "\n"
1893 "El-Torito boot options:\n"
1894 " -pN Boot platform ID in hex format (default: 00 for a BIOS system).\n"
1895 " -eN Boot media emulation. Valid values for 'N' are:\n"
1896 " 0 (or nothing): No emulation.\n"
1897 " 1: 1.2 MB diskette.\n"
1898 " 2: 1.44MB diskette.\n"
1899 " 3: 2.88MB diskette.\n"
1900 " 4: Hard disk.\n"
1901 " -b bootimage Create a single-boot El-Torito image.\n"
1902 " -bootdata: Create a multi-boot El-Torito image. This option cannot be\n"
1903 " combined with the -b option.\n"
1904 " Syntax:\n"
1905 " -bootdata:N#<defaultBootEntry>#<bootEntry2>#...#<bootEntryN>\n"
1906 " 'N': number of boot entries following.\n"
1907 " defaultBootEntry: The default boot entry, needed in all cases.\n"
1908 " Used by BIOSes which do not support additional boot entries.\n"
1909 " bootEntryX: Additional boot entries.\n"
1910 " - Do not use spaces.\n"
1911 " - Each multi-boot entry must be delimited with a hash symbol (#).\n"
1912 " - Each option for a boot entry must be delimited with a comma (,).\n"
1913 " - Each boot entry must specify the platform ID.\n";
1914
1915 /*-----------------------------------------------------------------------------
1916 Program execution starts here.
1917 -----------------------------------------------------------------------------*/
1918
1919 #if (defined(__GNUC__) || (_MSC_VER < 1900))
1920 char* strtok_s(char *str, const char *delim, char **ctx)
1921 {
1922 if (delim == NULL || ctx == NULL || (str == NULL && *ctx == NULL))
1923 {
1924 return NULL;
1925 }
1926
1927 if (!str)
1928 str = *ctx;
1929
1930 while (*str && strchr(delim, *str))
1931 str++;
1932 if (!*str)
1933 return NULL;
1934
1935 *ctx = str + 1;
1936 while (**ctx && !strchr(delim, **ctx))
1937 (*ctx)++;
1938 if (**ctx)
1939 *(*ctx)++ = '\0';
1940
1941 return str;
1942 }
1943 #endif
1944
1945 static void
1946 init_boot_entry(PBOOT_ENTRY boot_entry,
1947 BYTE boot_emu_type, WORD load_segment,
1948 char* bootimage)
1949 {
1950 boot_entry->boot_id = 0x88; // Bootable entry
1951 boot_entry->boot_emu_type = boot_emu_type; // 0: No emulation, etc...
1952 boot_entry->load_segment = load_segment; // If 0 then use default 0x07C0
1953
1954 boot_entry->bootimage[0] = '\0';
1955 strncpy(boot_entry->bootimage, bootimage, sizeof(boot_entry->bootimage));
1956 boot_entry->bootimage[sizeof(boot_entry->bootimage)-1] = '\0';
1957 }
1958
1959 int main(int argc, char **argv)
1960 {
1961 time_t timestamp = time(NULL);
1962 int i;
1963 char *t;
1964
1965 if (argc < 2)
1966 {
1967 puts(HELP);
1968 return 1;
1969 }
1970
1971 // Initialize CD-ROM write buffer
1972
1973 cd.file = NULL;
1974 cd.filespecs[0] = 0;
1975
1976 cd.buffer = malloc(BUFFER_SIZE);
1977 if (cd.buffer == NULL)
1978 error_exit("Insufficient memory");
1979
1980 // Initialize root directory
1981
1982 memset(&root, 0, sizeof(root));
1983 root.level = 1;
1984 root.flags = DIRECTORY_FLAG;
1985 convert_date_and_time(&root.date_and_time, &timestamp);
1986
1987 // Initialize parameters
1988
1989 scan_files_only = FALSE;
1990 make_bridged_udf = TRUE;
1991 // compute_crc = FALSE;
1992
1993 verbosity = NORMAL;
1994 show_progress = FALSE;
1995 size_limit = 0;
1996 accept_punctuation_marks = FALSE;
1997 source[0] = 0;
1998 volume_label[0] = 0;
1999
2000 // Initialize boot information
2001 eltorito = FALSE;
2002 multi_boot = FALSE;
2003 boot_validation_header.header_id = 1; // Validation header ID
2004 boot_validation_header.platform_id = 0; // x86/64 BIOS system
2005 init_boot_entry(&default_boot_entry,
2006 0, // No emulation
2007 0, // Use default 0x07C0
2008 "");
2009 default_boot_entry.sector_count = 0;
2010 default_boot_entry.load_rba = 0;
2011 boot_header_list = NULL;
2012
2013 // Scan command line arguments
2014
2015 for (i = 1; i < argc; i++)
2016 {
2017 if (strncmp(argv[i], "-v", 2) == 0)
2018 {
2019 t = argv[i] + 2;
2020 if (*t == 0) // Normal verbosity level.
2021 verbosity = NORMAL;
2022 else // Verbosity level in decimal
2023 verbosity = strtoul(t, NULL, 10);
2024
2025 // Check for validity
2026 if (verbosity > VERBOSE)
2027 verbosity = NORMAL;
2028
2029 // Disable by default, unless we are in normal verbosity level.
2030 // If progress is still wanted, use '-p'.
2031 if (verbosity == QUIET || verbosity == VERBOSE)
2032 show_progress = FALSE;
2033 }
2034 else if (strcmp(argv[i], "-p") == 0)
2035 show_progress = TRUE;
2036 else if (strncmp(argv[i], "-s", 2) == 0)
2037 {
2038 t = argv[i] + 2;
2039 if (*t == 0)
2040 {
2041 if (++i < argc)
2042 t = argv[i];
2043 else
2044 error_exit("Missing size limit parameter");
2045 }
2046 // size_limit = strtoul(t, NULL, 10);
2047 while (isdigit(*t))
2048 size_limit = size_limit * 10 + *t++ - '0';
2049 if (size_limit < 1 || size_limit > 800)
2050 error_exit("Invalid size limit");
2051 size_limit <<= 9; // convert megabyte to sector count
2052 }
2053 else if (strcmp(argv[i], "-m") == 0)
2054 accept_punctuation_marks = TRUE;
2055 else if (strcmp(argv[i], "-j") == 0)
2056 joliet = TRUE;
2057 else if (strncmp(argv[i], "-e", 2) == 0)
2058 {
2059 // Check whether the multi-boot option '-bootdata:' was already set.
2060 // If so, print an error and bail out.
2061 if (eltorito && multi_boot)
2062 error_exit("Single-boot and multi-boot entries cannot be combined");
2063
2064 eltorito = TRUE;
2065 multi_boot = FALSE;
2066
2067 t = argv[i] + 2;
2068 if (*t == 0) // No emulation
2069 default_boot_entry.boot_emu_type = 0;
2070 else // ID in decimal
2071 default_boot_entry.boot_emu_type = (BYTE)strtoul(t, NULL, 10);
2072 }
2073 else if (strncmp(argv[i], "-p", 2) == 0)
2074 {
2075 // Check whether the multi-boot option '-bootdata:' was already set.
2076 // If so, print an error and bail out.
2077 if (eltorito && multi_boot)
2078 error_exit("Single-boot and multi-boot entries cannot be combined");
2079
2080 eltorito = TRUE;
2081 multi_boot = FALSE;
2082
2083 // Platform ID in hexadecimal
2084 boot_validation_header.platform_id = (BYTE)strtoul(argv[i] + 2, NULL, 16);
2085 }
2086 else if (strcmp(argv[i], "-b") == 0)
2087 {
2088 // Check whether the multi-boot option '-bootdata:' was already set.
2089 // If so, print an error and bail out.
2090 if (eltorito && multi_boot)
2091 error_exit("Single-boot and multi-boot entries cannot be combined");
2092
2093 eltorito = TRUE;
2094 multi_boot = FALSE;
2095
2096 strncpy(default_boot_entry.bootimage, argv[++i], sizeof(default_boot_entry.bootimage));
2097 default_boot_entry.bootimage[sizeof(default_boot_entry.bootimage)-1] = '\0';
2098 }
2099 else if (strncmp(argv[i], "-bootdata:", sizeof("-bootdata:") - 1) == 0)
2100 {
2101 char *bootdata, *entry_ctx, *option_ctx;
2102 DWORD num_boot_entries = 0;
2103
2104 BOOL default_entry = TRUE; // Start by setting the default boot entry
2105 PBOOT_HEADER boot_header = NULL; // Current boot header
2106 PBOOT_ENTRY boot_entry = NULL; // The last boot entry in the current boot header
2107 BYTE platform_id, old_platform_id = 0;
2108 BYTE boot_emu_type;
2109 WORD load_segment;
2110 char bootimage[512];
2111
2112 // Check whether the single-boot option '-b' was already set.
2113 // If so, print an error and bail out.
2114 if (eltorito && !multi_boot)
2115 error_exit("Single-boot and multi-boot entries cannot be combined");
2116
2117 t = argv[i] + (sizeof("-bootdata:") - 1);
2118 bootdata = strdup(t);
2119 if (bootdata == NULL)
2120 error_exit("Insufficient memory");
2121
2122 eltorito = TRUE;
2123 multi_boot = TRUE;
2124
2125 // FIXME: Paths containing '#' or ',' or ' ' are not yet supported!!
2126
2127 // Start parsing...
2128 t = strtok_s(bootdata, "#", &entry_ctx);
2129 if (t == NULL)
2130 {
2131 free(bootdata);
2132 error_exit("Malformed bootdata command");
2133 }
2134
2135 num_boot_entries = strtoul(t, NULL, 10);
2136
2137 while (num_boot_entries--)
2138 {
2139 // Reset to default values
2140 platform_id = 0; // x86/64 BIOS system
2141 boot_emu_type = 0; // No emulation
2142 load_segment = 0; // Use default 0x07C0
2143 bootimage[0] = '\0';
2144
2145 t = strtok_s(NULL, "#", &entry_ctx);
2146 if (t == NULL)
2147 {
2148 free(bootdata);
2149 error_exit("Malformed bootdata command");
2150 }
2151
2152 t = strtok_s(t, ",", &option_ctx);
2153 while (t != NULL)
2154 {
2155 switch (*t++)
2156 {
2157 case 'b': // Boot sector file
2158 {
2159 char *q;
2160
2161 // Searches for any of the valid separators:
2162 // '#' starts a new boot entry;
2163 // ',' starts a new boot option;
2164 // ' ' finishes the bootdata command.
2165 q = strpbrk(t, "#, ");
2166 if (!q) q = t + strlen(t);
2167 strncpy(bootimage, t, q - t + 1);
2168 break;
2169 }
2170
2171 case 'p': // Platform ID
2172 {
2173 // Platform ID in hexadecimal
2174 platform_id = (BYTE)strtoul(t, NULL, 16);
2175 break;
2176 }
2177
2178 case 'e': // No floppy-disk emulation
2179 {
2180 if (*t == 0) // No emulation
2181 boot_emu_type = 0;
2182 else // ID in decimal
2183 boot_emu_type = (BYTE)strtoul(t, NULL, 10);
2184
2185 break;
2186 }
2187
2188 case 't': // Loading segment
2189 {
2190 if (*t == 0) // Not specified: use default 0x07C0
2191 load_segment = 0;
2192 else // Segment in hexadecimal
2193 load_segment = (BYTE)strtoul(t, NULL, 16);
2194
2195 break;
2196 }
2197
2198 default:
2199 free(bootdata);
2200 error_exit("Malformed bootdata command");
2201 }
2202
2203 t = strtok_s(NULL, ",", &option_ctx);
2204 }
2205
2206 // Create a new entry and possibly a boot header
2207 if (default_entry)
2208 {
2209 // Initialize the default boot entry and header
2210
2211 boot_validation_header.header_id = 1; // Validation header ID
2212 boot_validation_header.platform_id = platform_id;
2213
2214 init_boot_entry(&default_boot_entry, boot_emu_type, load_segment, bootimage);
2215
2216 // Default entry is now initialized.
2217 default_entry = FALSE;
2218 }
2219 else
2220 {
2221 // Initialize a new boot entry
2222 PBOOT_ENTRY old_boot_entry = boot_entry;
2223
2224 boot_entry = calloc(1, sizeof(*boot_entry));
2225 if (boot_entry == NULL)
2226 error_exit("Insufficient memory");
2227 // boot_entry->next_entry = NULL;
2228
2229 init_boot_entry(boot_entry, boot_emu_type, load_segment, bootimage);
2230
2231 // Create a new boot header if we don't have one yet
2232 if (boot_header == NULL)
2233 {
2234 boot_header = calloc(1, sizeof(*boot_header));
2235 if (boot_header == NULL)
2236 error_exit("Insufficient memory");
2237
2238 boot_header->header_id = 0x91; // So far this is the last boot header
2239 boot_header->platform_id = platform_id;
2240 // boot_header->next_header = NULL;
2241 // boot_header->num_entries = 0;
2242 // boot_header->entry_list = NULL;
2243
2244 old_boot_entry = NULL;
2245 old_platform_id = platform_id;
2246
2247 boot_header_list = boot_header;
2248 }
2249 else
2250 {
2251 // Create a new boot header if we change the platform ID
2252 if (old_platform_id != platform_id)
2253 {
2254 PBOOT_HEADER prev_boot_header = boot_header;
2255
2256 boot_header = calloc(1, sizeof(*boot_header));
2257 if (boot_header == NULL)
2258 error_exit("Insufficient memory");
2259
2260 boot_header->header_id = 0x91; // So far this is the last boot header
2261 boot_header->platform_id = platform_id;
2262 // boot_header->next_header = NULL;
2263 // boot_header->num_entries = 0;
2264 // boot_header->entry_list = NULL;
2265
2266 old_boot_entry = NULL;
2267 old_platform_id = platform_id;
2268
2269 // Link into the header list
2270 prev_boot_header->header_id = 0x90; // The previous boot header was not the last one
2271 prev_boot_header->next_header = boot_header;
2272 }
2273 }
2274
2275 // Add the entry into the header
2276 ++boot_header->num_entries;
2277 if (old_boot_entry == NULL)
2278 boot_header->entry_list = boot_entry;
2279 else
2280 old_boot_entry->next_entry = boot_entry;
2281 }
2282 }
2283
2284 free(bootdata);
2285 }
2286 else if (strcmp(argv[i], "-q") == 0)
2287 scan_files_only = TRUE;
2288 // else if (strcmp(argv[i], "-x") == 0)
2289 // compute_crc = TRUE;
2290 else if (i + 2 < argc)
2291 {
2292 strcpy(source, argv[i++]);
2293 strncpy(volume_label, argv[i++], sizeof(volume_label) - 1);
2294 strcpy(cd.filespecs, argv[i]);
2295 }
2296 else
2297 error_exit("Missing command line argument");
2298 }
2299
2300 if (source[0] == 0)
2301 error_exit("Missing source directory");
2302 if (volume_label[0] == 0)
2303 error_exit("Missing volume label");
2304 if (cd.filespecs[0] == 0)
2305 error_exit("Missing image file specifications");
2306
2307 if (source[0] != '@')
2308 {
2309 /* set source[] and end_source to source directory,
2310 * with a terminating directory separator */
2311 end_source = source + strlen(source);
2312 if (end_source[-1] == ':')
2313 *end_source++ = '.';
2314 if (end_source[-1] != DIR_SEPARATOR_CHAR)
2315 *end_source++ = DIR_SEPARATOR_CHAR;
2316
2317 /* scan all files and create directory structure in memory */
2318 make_directory_records(&root);
2319 }
2320 else
2321 {
2322 char *trimmedline, *targetname, *normdir, *srcname, *eq;
2323 char lineread[1024];
2324
2325 FILE *f = fopen(source+1, "r");
2326 if (!f)
2327 {
2328 error_exit("Cannot open CD-ROM file description '%s'\n", source+1);
2329 }
2330 while (fgets(lineread, sizeof(lineread), f))
2331 {
2332 /* We treat these characters as line endings */
2333 trimmedline = strtok(lineread, "\t\r\n;");
2334 eq = strchr(trimmedline, '=');
2335 if (!eq)
2336 {
2337 /* Treat this as a directory name */
2338 targetname = trimmedline;
2339 normdir = strdup(targetname);
2340 normalize_dirname(normdir);
2341 dir_hash_create_dir(&specified_files, targetname, normdir);
2342 free(normdir);
2343 }
2344 else
2345 {
2346 targetname = strtok(lineread, "=");
2347 srcname = strtok(NULL, "");
2348
2349 #ifdef _WIN32
2350 if (_access(srcname, R_OK) == 0)
2351 #else
2352 if (access(srcname, R_OK) == 0)
2353 #endif
2354 {
2355 if (!dir_hash_add_file(&specified_files, srcname, targetname))
2356 error_exit("Target '%s' (file '%s') is invalid\n", targetname, srcname);
2357 }
2358 else
2359 error_exit("Cannot access file '%s' (target '%s')\n", srcname, targetname);
2360 }
2361 }
2362 fclose(f);
2363
2364 /* scan all files and create directory structure in memory */
2365 scan_specified_files(&root, &specified_files.root);
2366 }
2367
2368 /* sort path table entries */
2369 root.next_in_path_table = sort_linked_list(root.next_in_path_table,
2370 1,
2371 compare_path_table_order);
2372
2373 // initialize CD-ROM write buffer
2374
2375 cd.file = NULL;
2376 cd.sector = 0;
2377 cd.offset = 0;
2378 cd.count = 0;
2379
2380 // make non-writing pass over directory structure to obtain the proper
2381 // sector numbers and offsets and to determine the size of the image
2382
2383 number_of_files = bytes_in_files = number_of_directories =
2384 bytes_in_directories = unused_bytes_at_ends_of_files = 0;
2385 pass();
2386
2387 if (verbosity >= NORMAL)
2388 {
2389 printf("%s bytes ", edit_with_commas(bytes_in_files, TRUE));
2390 printf("in %s files\n", edit_with_commas(number_of_files, FALSE));
2391 printf("%s unused bytes at ends of files\n",
2392 edit_with_commas(unused_bytes_at_ends_of_files, TRUE));
2393 printf("%s bytes ", edit_with_commas(bytes_in_directories, TRUE));
2394 printf("in %s directories\n",
2395 edit_with_commas(number_of_directories, FALSE));
2396 printf("%s other bytes\n", edit_with_commas(root.sector * SECTOR_SIZE, TRUE));
2397 puts("-------------");
2398 printf("%s total bytes\n",
2399 edit_with_commas(total_sectors * SECTOR_SIZE, TRUE));
2400 puts("=============");
2401 }
2402
2403 if (size_limit != 0 && total_sectors > size_limit)
2404 error_exit("Size limit exceeded");
2405
2406 if (!scan_files_only)
2407 {
2408 // re-initialize CD-ROM write buffer
2409
2410 cd.file = fopen(cd.filespecs, "w+b");
2411 if (cd.file == NULL)
2412 error_exit("Cannot open image file '%s'", cd.filespecs);
2413 cd.sector = 0;
2414 cd.offset = 0;
2415 cd.count = 0;
2416
2417
2418 // make writing pass over directory structure
2419
2420 pass();
2421
2422 if (cd.count > 0)
2423 flush_buffer();
2424 if (show_progress)
2425 printf("\r \n");
2426 if (fclose(cd.file) != 0)
2427 {
2428 cd.file = NULL;
2429 error_exit("File write error in image file '%s'", cd.filespecs);
2430 }
2431
2432 if (verbosity >= NORMAL)
2433 puts("CD-ROM image made successfully");
2434 }
2435
2436 dir_hash_destroy(&specified_files);
2437 release_memory();
2438 return 0;
2439 }
2440
2441 /* EOF */