diff --git a/reactos/tools/Makefile b/reactos/tools/Makefile index 225eacdfe04..9197219a0eb 100644 --- a/reactos/tools/Makefile +++ b/reactos/tools/Makefile @@ -13,7 +13,7 @@ TOOLS = \ CLEAN_FILES = $(TOOLS) -all: $(TOOLS) wmc_directory_target +all: $(TOOLS) wmc_directory_target cdmake_directory_target buildno$(EXE_POSTFIX): buildno.c ../include/reactos/version.h $(HOST_CC) $(CFLAGS) -o buildno$(EXE_POSTFIX) buildno.c @@ -83,14 +83,19 @@ endif wmc_directory_target: $(MAKE) -C wmc wmc$(EXE_POSTFIX) +cdmake_directory_target: + $(MAKE) -C cdmake cdmake$(EXE_POSTFIX) + ifeq ($(HOST),mingw32-linux) clean: + $(MAKE) -C cdmake clean $(MAKE) -C wmc clean rm mkconfig rm $(TOOLS) endif ifeq ($(HOST),mingw32-windows) clean: + $(MAKE) -C cdmake clean $(MAKE) -C wmc clean del *$(EXE_POSTFIX) endif diff --git a/reactos/tools/cdmake/.cvsignore b/reactos/tools/cdmake/.cvsignore new file mode 100644 index 00000000000..7165a1261f2 --- /dev/null +++ b/reactos/tools/cdmake/.cvsignore @@ -0,0 +1,10 @@ +cdmake +*.coff +*.d +*.exe +*.o +*.sym +*.dsp +*.dsw +*.ncb +*.opt diff --git a/reactos/tools/cdmake/Makefile b/reactos/tools/cdmake/Makefile new file mode 100644 index 00000000000..aa602bfc331 --- /dev/null +++ b/reactos/tools/cdmake/Makefile @@ -0,0 +1,37 @@ +# +# CD-Maker +# +PATH_TO_TOP = ../.. + +TARGET=cdmake$(EXE_POSTFIX) + +all: $(TARGET) + +OBJECTS = cdmake.o llmosrt.o + +CLEAN_FILES = *.o cdmake$(EXE_POSTFIX) + +cdmake$(EXE_POSTFIX): $(OBJECTS) + $(HOST_CC) $(OBJECTS) -o cdmake$(EXE_POSTFIX) + +HOST_CFLAGS = -I. + +#create_nls.o: create_nls.c +# $(HOST_CC) $(HOST_CFLAGS) -c create_nls.c -o create_nls.o + +ifeq ($(HOST),mingw32-linux) +clean: + rm -f *.o + rm -f cdmake$(EXE_POSTFIX) +endif +ifeq ($(HOST),mingw32-windows) +clean: + del *.o + del cdmake$(EXE_POSTFIX) +endif + +.phony: clean + +include $(PATH_TO_TOP)/rules.mak + +# EOF diff --git a/reactos/tools/cdmake/cdmake.c b/reactos/tools/cdmake/cdmake.c new file mode 100644 index 00000000000..5c90645c045 --- /dev/null +++ b/reactos/tools/cdmake/cdmake.c @@ -0,0 +1,1094 @@ +/* $Id: cdmake.c,v 1.1 2003/04/07 18:15:20 ekohl Exp $ */ +/* CD-ROM Maker + by Philip J. Erdelsky + pje@acm.org + http://www.alumni.caltech.edu/~pje/ + + ElTorito-Support + by Eric Kohl + ekohl@rz-online.de + */ + +/* According to his website, this file was released into the public domain by Phillip J. Erdelsky */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef int BOOL; + +const BOOL TRUE = 1; +const BOOL FALSE = 0; + +// file system parameters + +#define MAX_LEVEL 8 +#define MAX_NAME_LENGTH 8 +#define MAX_EXTENSION_LENGTH 3 +#define SECTOR_SIZE 2048 +#define BUFFER_SIZE (8 * SECTOR_SIZE) + +const BYTE HIDDEN_FLAG = 1; +const BYTE DIRECTORY_FLAG = 2; + + +struct cd_image +{ + FILE *file; + DWORD sector; // sector to receive next byte + int offset; // offset of next byte in sector + int count; // number of bytes in buffer + char filespecs[128]; + BYTE *buffer; +}; + +typedef struct date_and_time +{ + BYTE second; + BYTE minute; + BYTE hour; + BYTE day; + BYTE month; + WORD year; +} DATE_AND_TIME, *PDATE_AND_TIME; + +typedef struct directory_record +{ + struct directory_record *next_in_directory; + struct directory_record *next_in_path_table; /* directory record only */ + struct directory_record *next_in_memory; + struct directory_record *first_record; /* directory record only */ + struct directory_record *parent; + BYTE flags; + char name[MAX_NAME_LENGTH+1]; + char extension[MAX_EXTENSION_LENGTH+1]; + DATE_AND_TIME date_and_time; + DWORD sector; + DWORD size; + unsigned level; /* directory record only */ + WORD path_table_index; /* directory record only */ +} DIR_RECORD, *PDIR_RECORD; + + +typedef enum directory_record_type +{ + DOT_RECORD, + DOT_DOT_RECORD, + SUBDIRECTORY_RECORD, + FILE_RECORD +} DIR_RECORD_TYPE, *PDIR_RECORD_TYPE; + + +PDIR_RECORD sort_linked_list(PDIR_RECORD, + unsigned, int (*)(PDIR_RECORD, PDIR_RECORD)); + + +static char DIRECTORY_TIMESTAMP[] = "~Y$'KOR$.3K&"; + +static jmp_buf error; +static struct cd_image cd; + +char volume_label[32]; +DIR_RECORD root; +char source[512]; +char *end_source; +enum {QUIET, NORMAL, VERBOSE} verbosity; +BOOL show_progress; +DWORD size_limit; +BOOL accept_punctuation_marks; + +DWORD total_sectors; +DWORD path_table_size; +DWORD little_endian_path_table_sector; +DWORD big_endian_path_table_sector; +DWORD number_of_files; +DWORD bytes_in_files; +DWORD unused_bytes_at_ends_of_files; +DWORD number_of_directories; +DWORD bytes_in_directories; + +char bootimage[512]; +BOOL eltorito; +DWORD boot_catalog_sector; +DWORD boot_image_sector; + +/*----------------------------------------------------------------------------- +This function edits a 32-bit unsigned number into a comma-delimited form, such +as 4,294,967,295 for the largest possible number, and returns a pointer to a +static buffer containing the result. It suppresses leading zeros and commas, +but optionally pads the result with blanks at the left so the result is always +exactly 13 characters long (excluding the terminating zero). + +CAUTION: A statement containing more than one call on this function, such as +printf("%s, %s", edit_with_commas(a), edit_with_commas(b)), will produce +incorrect results because all calls use the same static bufffer. +-----------------------------------------------------------------------------*/ + +static char *edit_with_commas(DWORD x, BOOL pad) +{ + static char s[14]; + unsigned i = 13; + do + { + if (i % 4 == 2) s[--i] = ','; + s[--i] = x % 10 + '0'; + } while ((x/=10) != 0); + if (pad) + { + while (i > 0) s[--i] = ' '; + } + return s + i; +} + +/*----------------------------------------------------------------------------- +This function releases all allocated memory blocks. +-----------------------------------------------------------------------------*/ + +static void release_memory(void) +{ + while (root.next_in_memory != NULL) + { + struct directory_record *next = + root.next_in_memory->next_in_memory; + free (root.next_in_memory); + root.next_in_memory = next; + } + if (cd.buffer != NULL) + { + free (cd.buffer); + cd.buffer = NULL; + } +} + +/*----------------------------------------------------------------------------- +This function edits and displays an error message and then jumps back to the +error exit point in main(). +-----------------------------------------------------------------------------*/ + +static void error_exit(const char *format, ...) +{ + vfprintf(stderr, format, (char*)&format + 1); + fprintf(stderr, "\n"); + if (cd.file != NULL) + fclose(cd.file); + release_memory(); + longjmp(error, 1); +} + +/*----------------------------------------------------------------------------- +This function, which is called only on the second pass, and only when the +buffer is not empty, flushes the buffer to the CD-ROM image. +-----------------------------------------------------------------------------*/ + +static void flush_buffer(void) +{ + if (fwrite(cd.buffer, cd.count, 1, cd.file) < 1) + error_exit("File write error"); + cd.count = 0; + if (show_progress) + { + printf("\r%s ", + edit_with_commas((total_sectors - cd.sector) * SECTOR_SIZE, TRUE)); + } +} + +/*----------------------------------------------------------------------------- +This function writes a single byte to the CD-ROM image. On the first pass (in +which cd.handle < 0), it does not actually write anything but merely updates +the file pointer as though the byte had been written. +-----------------------------------------------------------------------------*/ + +static void write_byte(BYTE x) +{ + if (cd.file != NULL) + { + cd.buffer[cd.count] = x; + if (++cd.count == BUFFER_SIZE) + flush_buffer(); + } + if (++cd.offset == SECTOR_SIZE) + { + cd.sector++; + cd.offset = 0; + } +} + +/*----------------------------------------------------------------------------- +These functions write a word or double word to the CD-ROM image with the +specified endianity. +-----------------------------------------------------------------------------*/ + +static void write_little_endian_word(WORD x) +{ + write_byte(x); + write_byte(x >> 8); +} + +static void write_big_endian_word(WORD x) +{ + write_byte(x >> 8); + write_byte(x); +} + +static void write_both_endian_word(WORD x) +{ + write_little_endian_word(x); + write_big_endian_word(x); +} + +static void write_little_endian_dword(DWORD x) +{ + write_byte(x); + write_byte(x >> 8); + write_byte(x >> 16); + write_byte(x >> 24); +} + +static void write_big_endian_dword(DWORD x) +{ + write_byte(x >> 24); + write_byte(x >> 16); + write_byte(x >> 8); + write_byte(x); +} + +static void write_both_endian_dword(DWORD x) +{ + write_little_endian_dword(x); + write_big_endian_dword(x); +} + +/*----------------------------------------------------------------------------- +This function writes enough zeros to fill out the end of a sector, and leaves +the file pointer at the beginning of the next sector. If the file pointer is +already at the beginning of a sector, it writes nothing. +-----------------------------------------------------------------------------*/ + +static void fill_sector(void) +{ + while (cd.offset != 0) + write_byte(0); +} + +/*----------------------------------------------------------------------------- +This function writes a string to the CD-ROM image. The terminating \0 is not +written. +-----------------------------------------------------------------------------*/ + +static void write_string(char *s) +{ + while (*s != 0) + write_byte(*s++); +} + +/*----------------------------------------------------------------------------- +This function writes a block of identical bytes to the CD-ROM image. +-----------------------------------------------------------------------------*/ + +static void write_block(unsigned count, BYTE value) +{ + while (count != 0) + { + write_byte(value); + count--; + } +} + +/*----------------------------------------------------------------------------- +This function writes a directory record to the CD_ROM image. +-----------------------------------------------------------------------------*/ + +static void +write_directory_record(PDIR_RECORD d, + DIR_RECORD_TYPE DirType) +{ + unsigned identifier_size; + unsigned record_size; + + switch (DirType) + { + case DOT_RECORD: + case DOT_DOT_RECORD: + identifier_size = 1; + break; + case SUBDIRECTORY_RECORD: + identifier_size = strlen(d->name); + break; + case FILE_RECORD: + identifier_size = strlen(d->name) + 2; + if (d->extension[0] != 0) + identifier_size += 1 + strlen(d->extension); + break; + } + record_size = 33 + identifier_size; + if ((identifier_size & 1) == 0) + record_size++; + if (cd.offset + record_size > SECTOR_SIZE) + fill_sector(); + write_byte(record_size); + write_byte(0); // number of sectors in extended attribute record + write_both_endian_dword(d->sector); + write_both_endian_dword(d->size); + write_byte(d->date_and_time.year - 1900); + write_byte(d->date_and_time.month); + write_byte(d->date_and_time.day); + write_byte(d->date_and_time.hour); + write_byte(d->date_and_time.minute); + write_byte(d->date_and_time.second); + write_byte(0); // GMT offset + write_byte(d->flags); + write_byte(0); // file unit size for an interleaved file + write_byte(0); // interleave gap size for an interleaved file + write_both_endian_word((WORD) 1); // volume sequence number + write_byte(identifier_size); + switch (DirType) + { + case DOT_RECORD: + write_byte(0); + break; + case DOT_DOT_RECORD: + write_byte(1); + break; + case SUBDIRECTORY_RECORD: + write_string(d->name); + break; + case FILE_RECORD: + write_string(d->name); + if (d->extension[0] != 0) + { + write_byte('.'); + write_string(d->extension); + } + write_string(";1"); + break; + } + if ((identifier_size & 1) == 0) + write_byte(0); +} + +/*----------------------------------------------------------------------------- +This function converts the date and time words from an ffblk structure and +puts them into a date_and_time structure. +-----------------------------------------------------------------------------*/ + +static void convert_date_and_time(PDATE_AND_TIME dt, time_t *time) +{ + struct tm *timedef; + + timedef = localtime(time); + + dt->second = timedef->tm_sec; + dt->minute = timedef->tm_min; + dt->hour = timedef->tm_hour; + dt->day = timedef->tm_mday; + dt->month = timedef->tm_mon; + dt->year = timedef->tm_year + 1900; +} + +/*----------------------------------------------------------------------------- +This function checks the specified character, if necessary, and +generates an error if it is a punctuation mark other than an underscore. +It also converts small letters to capital letters and returns the +result. +-----------------------------------------------------------------------------*/ + +static int check_for_punctuation(int c, char *name) +{ + c = toupper(c & 0xFF); + if (!accept_punctuation_marks && !isalnum(c) && c != '_') + error_exit("Punctuation mark in %s", name); + return c; +} + +/*----------------------------------------------------------------------------- +This function creates a new directory record with the information from the +specified ffblk. It links it into the beginning of the directory list +for the specified parent and returns a pointer to the new record. +-----------------------------------------------------------------------------*/ + +PDIR_RECORD +new_directory_record (struct _finddata_t *f, + PDIR_RECORD parent) +{ + PDIR_RECORD d; + char *s; + char *t; + + d = malloc(sizeof(DIR_RECORD)); + if (d == NULL) + error_exit("Insufficient memory"); + d->next_in_memory = root.next_in_memory; + root.next_in_memory = d; + { + s = f->name; + t = d->name; + while (*s != 0) + { + if (*s == '.') + { + s++; + break; + } + *t++ = check_for_punctuation(*s++, f->name); + } + *t = 0; + t = d->extension; + while (*s != 0) + *t++ = check_for_punctuation(*s++, f->name); + *t = 0; + } + convert_date_and_time(&d->date_and_time, &f->time_create); + if (f->attrib & _A_SUBDIR) + { + if (d->extension[0] != 0) + error_exit("Directory with extension %s", f->name); + d->flags = DIRECTORY_FLAG; + } + else + d->flags = f->attrib & _A_HIDDEN ? HIDDEN_FLAG : 0; + d->size = f->size; + d->next_in_directory = parent->first_record; + parent->first_record = d; + d->parent = parent; + return d; +} + +/*----------------------------------------------------------------------------- +This function compares two directory records according to the ISO9660 rules +for directory sorting and returns a negative value if p is before q, or a +positive value if p is after q. +-----------------------------------------------------------------------------*/ + +static int compare_directory_order(PDIR_RECORD p, PDIR_RECORD q) +{ + int n = strcmp(p->name, q->name); + if (n == 0) + n = strcmp(p->extension, q->extension); + return n; +} + +/*----------------------------------------------------------------------------- +This function compares two directory records (which must represent +directories) according to the ISO9660 rules for path table sorting and returns +a negative value if p is before q, or a positive vlaue if p is after q. +-----------------------------------------------------------------------------*/ + +static int compare_path_table_order(PDIR_RECORD p, PDIR_RECORD q) +{ + if (p == q) + return 0; + int n = p->level - q->level; + if (n == 0) + { + n = compare_path_table_order(p->parent, q->parent); + if (n == 0) + n = compare_directory_order(p, q); + } + return n; +} + +/*----------------------------------------------------------------------------- +This function appends the specified string to the buffer source[]. +-----------------------------------------------------------------------------*/ + +static void append_string_to_source(char *s) +{ + while (*s != 0) + *end_source++ = *s++; +} + +/*----------------------------------------------------------------------------- +This function scans all files from the current source[] (which must end in \, +and represents a directory already in the database as d), +and puts the appropriate directory records into the database in memory, with +the specified root. It calls itself recursively to scan all subdirectories. +-----------------------------------------------------------------------------*/ + +static void +make_directory_records (PDIR_RECORD d) +{ + PDIR_RECORD new_d; + struct _finddata_t f; + char *old_end_source; + int findhandle; + + d->first_record = NULL; + strcpy(end_source, "*.*"); + + findhandle =_findfirst(source, &f); + if (findhandle != 0) + { + do + { + if ((f.attrib & (_A_HIDDEN | _A_SUBDIR)) == 0 && f.name[0] != '.') + { + if (strcmp(f.name, DIRECTORY_TIMESTAMP) == 0) + { + convert_date_and_time(&d->date_and_time, &f.time_create); + } + else + { + if (verbosity == VERBOSE) + { + old_end_source = end_source; + strcpy(end_source, f.name); + printf("%d: file %s\n", d->level, source); + end_source = old_end_source; + } + (void) new_directory_record(&f, d); + } + } + } + while (_findnext(findhandle, &f) == 0); + + _findclose(findhandle); + } + + strcpy(end_source, "*.*"); + findhandle= _findfirst(source, &f); + if (findhandle) + { + do + { + if (f.attrib & _A_SUBDIR && f.name[0] != '.') + { + old_end_source = end_source; + append_string_to_source(f.name); + *end_source++ = '\\'; + if (verbosity == VERBOSE) + { + *end_source = 0; + printf("%d: directory %s\n", d->level + 1, source); + } + if (d->level < MAX_LEVEL) + { + new_d = new_directory_record(&f, d); + new_d->next_in_path_table = root.next_in_path_table; + root.next_in_path_table = new_d; + new_d->level = d->level + 1; + make_directory_records(new_d); + } + else + { + error_exit("Directory is nested too deep"); + } + end_source = old_end_source; + } + } + while (_findnext(findhandle, &f) == 0); + + _findclose(findhandle); + } + + // sort directory + d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order); +} + +/*----------------------------------------------------------------------------- +This function loads the file specifications for the file or directory +corresponding to the specified directory record into the source[] buffer. It +is recursive. +-----------------------------------------------------------------------------*/ + +static void get_file_specifications(PDIR_RECORD d) +{ + if (d != &root) + { + get_file_specifications(d->parent); + append_string_to_source(d->name); + if ((d->flags & DIRECTORY_FLAG) == 0 && d->extension[0] != 0) + { + *end_source++ = '.'; + append_string_to_source(d->extension); + } + if (d->flags & DIRECTORY_FLAG) + *end_source++ = '\\'; + } +} + +static void pass(void) +{ + PDIR_RECORD d; + PDIR_RECORD q; + unsigned int i; + char *t; + unsigned int index; + unsigned int name_length; + DWORD size; + DWORD number_of_sectors; + char *old_end_source; + int n; + FILE *file; + + // first 16 sectors are zeros + + write_block(16 * SECTOR_SIZE, 0); + + // Primary Volume Descriptor + + write_string("\1CD001\1"); + write_byte(0); + write_block(32, ' '); // system identifier + + t = volume_label; + for (i = 0; i < 32; i++) + write_byte(*t != 0 ? toupper(*t++) : ' '); + + write_block(8, 0); + write_both_endian_dword(total_sectors); + write_block(32, 0); + write_both_endian_word((WORD) 1); // volume set size + write_both_endian_word((WORD) 1); // volume sequence number + write_both_endian_word((WORD) 2048); // sector size + write_both_endian_dword(path_table_size); + write_little_endian_dword(little_endian_path_table_sector); + write_little_endian_dword((DWORD) 0); // second little endian path table + write_big_endian_dword(big_endian_path_table_sector); + write_big_endian_dword((DWORD) 0); // second big endian path table + write_directory_record(&root, DOT_RECORD); + write_block(128, ' '); // volume set identifier + write_block(128, ' '); // publisher identifier + write_block(128, ' '); // data preparer identifier + write_block(128, ' '); // application identifier + write_block(37, ' '); // copyright file identifier + write_block(37, ' '); // abstract file identifier + write_block(37, ' '); // bibliographic file identifier + write_string("0000000000000000"); // volume creation + write_byte(0); + write_string("0000000000000000"); // most recent modification + write_byte(0); + write_string("0000000000000000"); // volume expires + write_byte(0); + write_string("0000000000000000"); // volume is effective + write_byte(0); + write_byte(1); + write_byte(0); + fill_sector(); + + + // Boot Volume Descriptor + + if (eltorito == TRUE) + { + write_byte(0); + write_string("CD001\1"); + write_string("EL TORITO SPECIFICATION"); // identifier + write_block(9, 0); // padding + write_block(32, 0); // unused + write_little_endian_dword(boot_catalog_sector); // pointer to boot catalog + fill_sector(); + } + + + // Volume Descriptor Set Terminator + + write_string("\377CD001\1"); + fill_sector(); + + + // Boot Catalog + + if (eltorito == TRUE) + { + boot_catalog_sector = cd.sector; + + // Validation entry + write_byte(1); + write_byte(0); // x86 boot code + write_little_endian_word(0); // reserved + write_string("ReactOS Foundation"); + write_block(6, 0); // padding + write_little_endian_word(0x62E); // checksum + write_little_endian_word(0xAA55); // signature + + // default entry + write_byte(0x88); // bootable + write_byte(0); // no emulation + write_big_endian_word(0); // load segment = default (0x07c0) + write_byte(0); // partition type + write_byte(0); // unused + write_little_endian_word(4); // sector count + write_little_endian_dword(boot_image_sector); // sector count + + fill_sector(); + } + + + // Boot Image + + if (eltorito == TRUE) + { + boot_image_sector = cd.sector; + + file = fopen(bootimage, "rb"); + if (file == NULL) + error_exit("Can't open %s\n", bootimage); + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + while (size > 0) + { + n = BUFFER_SIZE - cd.count; + if ((DWORD) n > size) + n = size; + if (fread (cd.buffer + cd.count, n, 1, file) < 1) + { + fclose(file); + error_exit("Read error in file %s\n", bootimage); + } + cd.count += n; + if (cd.count == BUFFER_SIZE) + flush_buffer(); + cd.sector += n / SECTOR_SIZE; + cd.offset += n % SECTOR_SIZE; + size -= n; + } + fclose(file); +// fill_sector(); + } + + + // Little Endian Path Table + + little_endian_path_table_sector = cd.sector; + write_byte(1); + write_byte(0); // number of sectors in extended attribute record + write_little_endian_dword(root.sector); + write_little_endian_word((WORD) 1); + write_byte(0); + write_byte(0); + + index = 1; + root.path_table_index = 1; + for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table) + { + name_length = strlen(d->name); + write_byte(name_length); + write_byte(0); // number of sectors in extended attribute record + write_little_endian_dword(d->sector); + write_little_endian_word(d->parent->path_table_index); + write_string(d->name); + if (name_length & 1) + write_byte(0); + d->path_table_index = ++index; + } + + path_table_size = (cd.sector - little_endian_path_table_sector) * + SECTOR_SIZE + cd.offset; + fill_sector(); + + // Big Endian Path Table + + big_endian_path_table_sector = cd.sector; + write_byte(1); + write_byte(0); // number of sectors in extended attribute record + write_big_endian_dword(root.sector); + write_big_endian_word((WORD) 1); + write_byte(0); + write_byte(0); + + for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table) + { + name_length = strlen(d->name); + write_byte(name_length); + write_byte(0); // number of sectors in extended attribute record + write_big_endian_dword(d->sector); + write_big_endian_word(d->parent->path_table_index); + write_string(d->name); + if (name_length & 1) + write_byte(0); + } + fill_sector(); + + + // directories and files + + for (d = &root; d != NULL; d = d->next_in_path_table) + { + // write directory + + d->sector = cd.sector; + write_directory_record(d, DOT_RECORD); + write_directory_record(d == &root ? d : d->parent, DOT_DOT_RECORD); + for (q = d->first_record; q != NULL; q = q->next_in_directory) + { + write_directory_record(q, + q->flags & DIRECTORY_FLAG ? SUBDIRECTORY_RECORD : FILE_RECORD); + } + fill_sector(); + d->size = (cd.sector - d->sector) * SECTOR_SIZE; + number_of_directories++; + bytes_in_directories += d->size; + + // write file data + + for (q = d->first_record; q != NULL; q = q->next_in_directory) + { + if ((q->flags & DIRECTORY_FLAG) == 0) + { + q->sector = cd.sector; + size = q->size; + if (cd.file == NULL) + { + number_of_sectors = (size + SECTOR_SIZE - 1) / SECTOR_SIZE; + cd.sector += number_of_sectors; + number_of_files++; + bytes_in_files += size; + unused_bytes_at_ends_of_files += + number_of_sectors * SECTOR_SIZE - size; + } + else + { + old_end_source = end_source; + get_file_specifications(q); + *end_source = 0; + if (verbosity == VERBOSE) + printf("Writing %s\n", source); + file = fopen(source, "rb"); + if (file == NULL) + error_exit("Can't open %s\n", source); + fseek(file, 0, SEEK_SET); + while (size > 0) + { + n = BUFFER_SIZE - cd.count; + if ((DWORD) n > size) + n = size; + if (fread (cd.buffer + cd.count, n, 1, file) < 1) + { + fclose(file); + error_exit("Read error in file %s\n", source); + } + cd.count += n; + if (cd.count == BUFFER_SIZE) + flush_buffer(); + cd.sector += n / SECTOR_SIZE; + cd.offset += n % SECTOR_SIZE; + size -= n; + } + fclose(file); + end_source = old_end_source; + fill_sector(); + } + } + } + } + + total_sectors = (DWORD)cd.sector; +} + +static char HELP[] = + "CDMAKE [-q] [-v] [-p] [-s N] [-m] [-b bootimage] source volume image\n" + "\n" + " source specifications of base directory containing all files to\n" + " be written to CD-ROM image\n" + " volume volume label\n" + " image image file or device\n" + " -q quiet mode - display nothing but error messages\n" + " -v verbose mode - display file information as files are\n" + " scanned and written - overrides -p option\n" + " -p show progress while writing\n" + " -s N abort operation before beginning write if image will be\n" + " larger than N megabytes (i.e. 1024*1024*N bytes)\n" + " -m accept punctuation marks other than underscores in\n" + " names and extensions\n" + " -b bootimage create bootable ElTorito CD-ROM using 'no emulation' mode\n"; + +/*----------------------------------------------------------------------------- +Program execution starts here. +-----------------------------------------------------------------------------*/ + +int main(int argc, char **argv) +{ + BOOL q_option = FALSE; + BOOL v_option = FALSE; + int i; + char *t; + + if (argc < 2) + { + puts(HELP); + return 1; + } + + if (setjmp(error)) + return 1; + + // initialize root directory + + cd.buffer = malloc (BUFFER_SIZE); + if (cd.buffer == NULL) + error_exit("Insufficient memory"); + + memset(&root, 0, sizeof(root)); + root.level = 1; + root.flags = DIRECTORY_FLAG; + + // initialize CD-ROM write buffer + + cd.file = NULL; + cd.filespecs[0] = 0; + + // initialize parameters + + verbosity = NORMAL; + size_limit = 0; + show_progress = FALSE; + accept_punctuation_marks = FALSE; + source[0] = 0; + volume_label[0] = 0; + + eltorito = FALSE; + + // scan command line arguments + + for (i = 1; i < argc; i++) + { + if (memcmp(argv[i], "-s", 2) == 0) + { + t = argv[i] + 2; + if (*t == 0) + { + if (++i < argc) + t = argv[i]; + else + error_exit("Missing size limit parameter"); + } + while (isdigit(*t)) + size_limit = size_limit * 10 + *t++ - '0'; + if (size_limit < 1 || size_limit > 800) + error_exit("Invalid size limit"); + size_limit <<= 9; // convert megabyte to sector count + } + else if (strcmp(argv[i], "-q") == 0) + q_option = TRUE; + else if (strcmp(argv[i], "-v") == 0) + v_option = TRUE; + else if (strcmp(argv[i], "-p") == 0) + show_progress = TRUE; + else if (strcmp(argv[i], "-m") == 0) + accept_punctuation_marks = TRUE; + else if (strcmp(argv[i], "-b") == 0) + { + strcpy(bootimage, argv[++i]); + eltorito = TRUE; + } + else if (i + 2 < argc) + { + strcpy(source, argv[i++]); + strncpy(volume_label, argv[i++], sizeof(volume_label) - 1); + strcpy(cd.filespecs, argv[i]); + } + else + error_exit("Missing command line argument"); + } + if (v_option) + { + show_progress = FALSE; + verbosity = VERBOSE; + } + else if (q_option) + { + verbosity = QUIET; + show_progress = FALSE; + } + if (source[0] == 0) + error_exit("Missing source directory"); + if (volume_label[0] == 0) + error_exit("Missing volume label"); + if (cd.filespecs[0] == 0) + error_exit("Missing image file specifications"); + + + // set source[] and end_source to source directory, with a terminating \ + + end_source = source + strlen(source); + if (end_source[-1] == ':') + *end_source++ = '.'; + if (end_source[-1] != '\\') + *end_source++ = '\\'; + + // scan all files and create directory structure in memory + + make_directory_records(&root); + + // sort path table entries + + root.next_in_path_table = sort_linked_list(root.next_in_path_table, 1, + compare_path_table_order); + + // initialize CD-ROM write buffer + + cd.file = NULL; + cd.sector = 0; + cd.offset = 0; + cd.count = 0; + + // make non-writing pass over directory structure to obtain the proper + // sector numbers and offsets and to determine the size of the image + + number_of_files = bytes_in_files = number_of_directories = + bytes_in_directories = unused_bytes_at_ends_of_files =0; + pass(); + + if (verbosity >= NORMAL) + { + printf("%s bytes ", edit_with_commas(bytes_in_files, TRUE)); + printf("in %s files\n", edit_with_commas(number_of_files, FALSE)); + printf("%s unused bytes at ends of files\n", + edit_with_commas(unused_bytes_at_ends_of_files, TRUE)); + printf("%s bytes ", edit_with_commas(bytes_in_directories, TRUE)); + printf("in %s directories\n", + edit_with_commas(number_of_directories, FALSE)); + printf("%s other bytes\n", edit_with_commas(root.sector * SECTOR_SIZE, TRUE)); + puts("-------------"); + printf("%s total bytes\n", + edit_with_commas(total_sectors * SECTOR_SIZE, TRUE)); + puts("============="); + } + + if (size_limit != 0 && total_sectors > size_limit) + error_exit("Size limit exceeded"); + + // re-initialize CD-ROM write buffer + + cd.file = fopen(cd.filespecs, "w+b"); + if (cd.file == NULL) + error_exit("Can't open image file %s", cd.filespecs); + cd.sector = 0; + cd.offset = 0; + cd.count = 0; + + + // make writing pass over directory structure + + pass(); + + if (cd.count > 0) + flush_buffer(); + if (show_progress) + printf("\r \n"); + if (fclose(cd.file) != 0) + { + cd.file = NULL; + error_exit("File write error in image file %s", cd.filespecs); + } + + if (verbosity >= NORMAL) + puts("CD-ROM image made successfully"); + + release_memory(); + return 0; +} + +/* EOF */ diff --git a/reactos/tools/cdmake/llmosrt.c b/reactos/tools/cdmake/llmosrt.c new file mode 100644 index 00000000000..82e562a5b4a --- /dev/null +++ b/reactos/tools/cdmake/llmosrt.c @@ -0,0 +1,106 @@ +/* $Id: llmosrt.c,v 1.1 2003/04/07 18:15:20 ekohl Exp $ */ +/* A Linked-List Memory Sort + by Philip J. Erdelsky + pje@acm.org + http://www.alumni.caltech.edu/~pje/ +*/ + +/* According to his website, this file was released into the public domain by Phillip J. Erdelsky */ + + +#include + +void *sort_linked_list(void *p, unsigned index, int (*compare)(void *, void *)) +{ + unsigned base; + unsigned long block_size; + + struct record + { + struct record *next[1]; + /* other members not directly accessed by this function */ + }; + + struct tape + { + struct record *first, *last; + unsigned long count; + } tape[4]; + + /* Distribute the records alternately to tape[0] and tape[1]. */ + + tape[0].count = tape[1].count = 0L; + tape[0].first = NULL; + base = 0; + while (p != NULL) + { + struct record *next = ((struct record *)p)->next[index]; + ((struct record *)p)->next[index] = tape[base].first; + tape[base].first = ((struct record *)p); + tape[base].count++; + p = next; + base ^= 1; + } + + /* If the list is empty or contains only a single record, then */ + /* tape[1].count == 0L and this part is vacuous. */ + + for (base = 0, block_size = 1L; tape[base+1].count != 0L; + base ^= 2, block_size <<= 1) + { + int dest; + struct tape *tape0, *tape1; + tape0 = tape + base; + tape1 = tape + base + 1; + dest = base ^ 2; + tape[dest].count = tape[dest+1].count = 0; + for (; tape0->count != 0; dest ^= 1) + { + unsigned long n0, n1; + struct tape *output_tape = tape + dest; + n0 = n1 = block_size; + while (1) + { + struct record *chosen_record; + struct tape *chosen_tape; + if (n0 == 0 || tape0->count == 0) + { + if (n1 == 0 || tape1->count == 0) + break; + chosen_tape = tape1; + n1--; + } + else if (n1 == 0 || tape1->count == 0) + { + chosen_tape = tape0; + n0--; + } + else if ((*compare)(tape0->first, tape1->first) > 0) + { + chosen_tape = tape1; + n1--; + } + else + { + chosen_tape = tape0; + n0--; + } + chosen_tape->count--; + chosen_record = chosen_tape->first; + chosen_tape->first = chosen_record->next[index]; + if (output_tape->count == 0) + output_tape->first = chosen_record; + else + output_tape->last->next[index] = chosen_record; + output_tape->last = chosen_record; + output_tape->count++; + } + } + } + + if (tape[base].count > 1L) + tape[base].last->next[index] = NULL; + return tape[base].first; +} + +/* EOF */ diff --git a/reactos/tools/cdmake/readme.txt b/reactos/tools/cdmake/readme.txt new file mode 100644 index 00000000000..fd0301b30f8 --- /dev/null +++ b/reactos/tools/cdmake/readme.txt @@ -0,0 +1,94 @@ + CD-ROM Maker + Philip J. Erdelsky + +The CDMAKE utility converts files from DOS/Windows format to ISO9660 +(CD-ROM) format. + +First, gather all the files to be converted and put them into a single +base directory and its subdirectories, arranged just the way you want +them on the CD-ROM. Remember that ISO9660 allows subdirectories to be +nested only eight levels deep. Therefore, if the base directory is +C:\CDROM, + + C:\CDROM\D2\D3\D4\D5\D6\D7\D8\FOO.TXT is permitted, but + + C:\CDROM\D2\D3\D4\D5\D6\D7\D8\D9\FOO.TXT is forbidden. + +Also, ISO9660 does not allow directories to have extensions, although +DOS does. + +Finally, the characters in file and directory names and file extensions +must be letters, digits or underscores. Other punctuation marks +permitted by DOS/Windows are forbidden by ISO9660. You can use the -c +option to override this restriction, but the resulting CD-ROM may not be +readable on systems other than DOS/Windows. + +Files in the base directory will be written to the root directory of the +CD-ROM image. All subdirectories of the base directory will appear as +subdirectories of the root directory of the CD-ROM image. Their +contents, and the contents of their subdirectories, down to the eighth +level, will be faithfully copied to the CD-ROM image. + +System files will not be written to the CD-ROM image. Hidden files will +be written to the CD-ROM image, and will retain their hidden attributes. +Read-only files will be written, and will remain read-only on the +CD-ROM, but this does not distinguish them in any way, because on a +CD-ROM all files are read-only. The archive attribute will be lost. + +File and directory date and time stamps will be preserved in the CD-ROM +image. + +The utility is called up by a command line of the following form: + + CDMAKE [-q] [-v] [-p] [-s N] [-m] [-b bootimage] source volume image + + source specifications of base directory containing all files to + be written to CD-ROM image + + volume volume label + + image image file or device + + -q quiet mode - display nothing but error messages + + -v verbose mode - display file information as files are + scanned and written - overrides -p option + + -p show progress while writing + + -s N abort operation before beginning write if image will be + larger than N megabytes (i.e. 1024*1024*N bytes) + + -m accept punctuation marks other than underscores in + names and extensions + + -b bootimage create bootable ElTorito CD-ROM using 'no emulation' mode + + +The utility makes three passes over the source files: + + (1) The scanning pass, in which the names and extensions are + checked for validity, and the names, extensions, sizes, dates, + times and attributes are recorded internally. The files are not + actually read during this pass. + + (2) The layout pass, in which the sizes and positions of + directories, files and other items in the CD-ROM image are + determined. + + (3) The writing pass, in which the files are actually read and the + CD-ROM image is actually written to the specified file or + device. The image is always written sequentially. + +If neither the -q nor the -v option is used, CDMAKE will display the +volume label, size, number of files and directories and the total bytes +in each at the end of the layout pass. + +If the -p option is used, and is not overridden by the -v option, then +during the writing pass, CDMAKE will display the number of bytes still +to be written to the CD-ROM image, updating it frequently. The number +will decrease as the operation progresses, and will reach zero when the +operation is complete. + +The operation of CDMAKE can be aborted by typing Ctrl-C when the utility +is displaying text of any kind.