/* Convert an EBS file into another EBS file with different data part encoding. Copyright (C) 1993/94 Markus Kuhn, Institut fuer Physiologie und Biokybernetik (IPB), Universitaet Erlangen, Deutschland Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and related publications and that both that copyright notice and this permission notice appear in supporting documentation. The authors make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. Send your comments, suggestions or bug reports to ftpebs@uni-erlangen.de Or mail to Institut fuer Physiologie und Biokybernetik Markus Prosch Universitaetsstrasse 17 D-91054 Erlangen Germany $Id: ebsconv.c,v 1.7 1994/03/01 11:08:31 msprosch Exp $ */ #include #include #include #include #include #include #include "ebs.h" #ifdef MSDOS #include #include typedef unsigned mode_t; #define O_NOCTTY 0 #define S_IRUSR S_IREAD #define S_IWUSR S_IWRITE #define S_IRGRP S_IREAD #define S_IWGRP S_IWRITE #define S_IROTH S_IREAD #define S_IWOTH S_IWRITE #else #include /* for POSIX systems */ #include #endif /* MSDOS */ #ifdef MSDOS #include "setstack.c" #endif /* for pre-ANSI-versions of stdio.h */ #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef SEEK_CUR #define SEEK_CUR 1 #endif char copyright[]= "Convert an EBS file into another EBS file with different data part encoding.\n" "Copyright (C) 1993/94 Markus Kuhn, Institut fuer Physiologie und\n" " Biokybernetik (IPB), Universitaet Erlangen, Deutschland\n\n" "Permission to use, copy, modify, distribute, and sell this software\n" "and its documentation for any purpose is hereby granted without fee,\n" "provided that the above copyright notice appear in all copies and\n" "related publications and that both that copyright notice and this\n" "permission notice appear in supporting documentation. The authors make\n" "no representations about the suitability of this software for any\n" "purpose. It is provided \"as is\" without express or implied warranty.\n\n" "Send your comments, suggestions or bug reports to\n" " ftpebs@uni-erlangen.de\n\n" "Or mail to\n" " Institut fuer Physiologie und Biokybernetik\n" " Markus Prosch\n" " Universitaetsstrasse 17\n" " D-91054 Erlangen\n" " Germany\n\n"; char version[] = "EBS encoding converter, V0.3\n" "(C) 1993/94, Markus Kuhn, IPB, Erlangen"; char usage[] = "%s\n\nusage: %s [-bfip] [-s " "]\n []\n" "\npossible encodings:\n" "\tTIB_16 time order, 16-bit Bigendian integer values\n" "\tCIB_16 channel order, 16-bit Bigendian integer values\n" "\tTIL_16 time order, 16-bit Littleendian integer values\n" "\tCIL_16 channel order, 16-bit Littleendian integer values\n" "\tTI_16D time order, 16-bit compressed integer values\n" "\tCI_16D channel order, 16-bit compressed integer values\n\n" "Recommended standard encodings are CIB_16 and TI_16D for compressed files." "\n\noptions:\n" "\t-c print out copyright notice\n" "\t-h Print out this message\n" "\t-s The input file consists only of data (no headers)\n" "\t in the specified input encoding. Also the number\n" "\t of channels in the input file and the number of samples\n" "\t have to be specified after -s.\n" "\t The number of samples is ignored for time-based order\n" "\t encodings.\n" "\t-b Put all variable header content behind the data part.\n" "\t-f Put all variable header content in front of the data part.\n" "\t-i Remove all IGNORE attributes.\n" "\t-p The output file consists only of data bytes, no headers\n" "\t will be written.\n\n"; /* global variables */ FILE *fin, *fout; char *fnout = NULL, *fnin = NULL; /* filenames */ /* allocate memory for a string with a path of a non-existing temp. file in the same directory as oldname and create an empty file with this name (in order to aviod a race condition. Works also under MS-DOS. */ char *newfilename(char *oldname, mode_t mode) { static char *newname; int i, j = 0; int fd; extern int errno; newname = (char *) malloc(strlen(oldname) + 13); if (newname == NULL) { fprintf(stderr, "Not enough memory!\n"); exit(1); } strcpy(newname, oldname); for (i = 0; newname[i] != '\0'; i++) if (newname[i] == '/' || newname[i] == '\\' || newname[i] == ':') j = i + 1; i = 0; do { sprintf(newname + j, "ec%06x.tmp", i++); fd = open(newname, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, mode); } while (fd < 0 && errno == EEXIST); if (fd < 0) { fprintf(stderr, "Can't open temporary file '%s", newname); perror("'"); exit(1); } close(fd); return newname; } /* Copy n bytes from fin to fout */ void fcopy(unsigned long n) { #define BUFSIZE 4096 char buf[BUFSIZE]; unsigned long i = 0, t; while (i < n) { t = n - i; if (t > BUFSIZE) t = BUFSIZE; fread(buf, 1, t, fin); fwrite(buf, 1, t, fout); i += t; } } /* Copy a variable header part from fin to fout without the final tag. If noignore is not 0, EBS_IGNORE attributes are removed. Return value is the number of bytes written to fout. */ long fvhcopy(int noignore) { unsigned long tag, attribute_length; long bytes = 0; while ((tag = fgeti32(fin)) != EBS_FINAL_TAG) { attribute_length = fgeti32(fin); if (noignore && tag == EBS_IGNORE) { if (fseek(fin, attribute_length * 4, SEEK_CUR)) { perror("Error occured while copying variable header"); fclose(fout); remove(fnout); exit(1); } } else { fputi32(tag, fout); fputi32(attribute_length, fout); fcopy(attribute_length * 4); bytes += 8 + attribute_length * 4; } } return bytes; } main(int argc, char **argv) { char vhead = 'd'; /* pos. of variable header: (f)ront, (b)ehind, (d)efault */ int noignore = 0; long incode = (long) 0xffffffffL, outcode = (long) 0xffffffffL; long channels = 0; unsigned long samples_hi = 0, samples, length_hi = 0, length; unsigned long old_samples; int i, j; int fd; int secondout = 0; /* is 1 if a seperate output file has been specified */ int input_header = 1; /* 0 if no input header present */ int output_header = 1; /* 0 if no output header shall be written */ long header_length = 0, v2_start = 0; /* info about var. header */ long header_outlength = 0, newlength; unsigned long attribute_length; mode_t mode = 0; struct stat statbuf; /* do all the command line and file open stuff */ fin = NULL; fout = NULL; for (i = 1; i < argc; i++) { if (argv[i][0] == '/' && argv[i][1] == '?' && argv[i][2] == 0) { fprintf(stderr, usage, version, argv[0]); exit(1); } else if (argv[i][0] == '-') for (j = 1; j < 20 && argv[i][j] != 0; j++) switch (argv[i][j]) { case 's': case 'S': input_header = 0; j = 9999; if (i >= argc - 3) { fprintf(stderr, "Expecting input encoding, number of channels\n" "and number of samples after option -s!\n"); exit(1); } i++; if ((incode = formatid(argv[i])) == 0xffffffffL) { fprintf(stderr, "Unknown input file encoding '%s' after " "option -s!\n", argv[i]); exit(1); } i++; channels = atoi(argv[i]); if (channels <= 0) { fprintf(stderr, "Incorrect number of channels '%s' after " "option -s!\n", argv[i]); exit(1); } i++; samples = samples_hi = 0xffffffffL; if (incode == EBS_CIB_16 || incode == EBS_CIL_16 || incode == EBS_CI_16D) { samples_hi = 0; samples = atoi(argv[i]); if (((long) samples) <= 0) { fprintf(stderr, "Incorrect number of samples '%s' after " "option -s!\n", argv[i]); exit(1); } } break; case 'f': case 'F': vhead = 'f'; break; case 'b': case 'B': vhead = 'b'; break; case 'i': case 'I': noignore = 1; break; case 'p': case 'P': output_header = 0; break; case 'c': case 'C': fprintf (stderr, copyright); exit (1); default: fprintf(stderr, usage, version, argv[0]); exit(1); } else if (outcode == 0xffffffffL) { if ((outcode = formatid(argv[i])) == 0xffffffffL) { fprintf(stderr, "Unknown output file encoding '%s'!\n", argv[i]); exit(1); } } else if (fin == NULL) { fnin = argv[i]; fin = fopen(fnin, "rb"); if (fin == NULL) { fprintf(stderr, "Can't open input file '%s", argv[i]); perror("'"); exit(1); } mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; if (stat(fnin, &statbuf) == 0) mode = statbuf.st_mode; else perror("stat() failed"); } else if (fout == NULL) { /* first make sure the output file doesn't exist */ fd = open(argv[i], O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, mode | S_IRUSR | S_IWUSR); if (fd < 0) { fprintf(stderr, "Can't open output file '%s", argv[i]); perror("'"); exit(1); } close(fd); fout = fopen(argv[i], "wb"); if (fout == NULL) { fprintf(stderr, "Can't open output file '%s", argv[i]); perror("'"); exit(1); } fnout = argv[i]; secondout = 1; } else { fprintf(stderr, usage, version, argv[0]); exit(1); } } if (argc < 3) { fprintf(stderr, usage, version, argv[0]); exit(1); } if (outcode == 0xffffffffL || fin == NULL) { fprintf(stderr, usage, version, argv[0]); exit(1); } if (!secondout) { /* test for write rights on input file */ fd = open(fnin, O_RDWR | O_NOCTTY); if (fd < 0) { fprintf(stderr, "Can't overwrite file '%s", fnin); perror("'"); exit(1); } close(fd); /* open temporary output file */ fnout = newfilename(fnin, mode | S_IRUSR | S_IWUSR); fout = fopen(fnout, "wb"); if (fout == NULL) { fprintf(stderr, "Can't open temporary file '%s", fnout); perror("'"); exit(1); } } /* read input file */ if (input_header) { if (fgeti32(fin) != EBS_MAGIC1 || fgeti32(fin) != EBS_MAGIC2 || feof(fin)) { fprintf(stderr, "Input file '%s' is not in EBS format!\n", fnin); fclose(fout); remove(fnout); exit(1); } incode = fgeti32(fin); if (incode != EBS_TIB_16 && incode != EBS_CIB_16 && incode != EBS_TIL_16 && incode != EBS_CIL_16 && incode != EBS_TI_16D && incode != EBS_CI_16D) { fprintf(stderr, "Input EBS file '%s' has an unknown data part encoding " "(0x%08lx)!\n", incode); fclose(fout); remove(fnout); exit(1); } channels = fgeti32(fin); samples_hi = fgeti32(fin); samples = fgeti32(fin); if ((samples_hi != 0 && (samples_hi != 0xffffffffL || samples != 0xffffffffL)) || (samples_hi == 0 && samples > 0xfffffffcL)) { fprintf(stderr, "Input EBS file '%s' is too long!\n", fnin); fclose(fout); remove(fnout); exit(1); } length_hi = fgeti32(fin); length = fgeti32(fin); if (length_hi != 0 && (length_hi != 0xffffffffL || length != 0xffffffffL)) { fprintf(stderr, "Input EBS file '%s' is too long!\n", fnin); fclose(fout); remove(fnout); exit(1); } if (samples == 0xffffffffL) { if (incode == EBS_CIB_16 || incode == EBS_CIL_16 || incode == EBS_CI_16D) { fprintf(stderr, "Input EBS file '%s' uses channel order\n" "and has an unspecified number of samples!\n", fnin); fclose(fout); remove(fnout); exit(1); } if (length_hi != 0xffffffffL) { fprintf(stderr, "Input EBS file '%s' has a second variable\n" "header part and has an unspecified number of samples!\n", fnin); fclose(fout); remove(fnout); exit(1); } } /* examine variable header */ header_length = 32 + 4; while (fgeti32(fin) != EBS_FINAL_TAG) { attribute_length = fgeti32(fin); header_length += 8 + attribute_length * 4; if (fseek(fin, attribute_length * 4, SEEK_CUR)) { fprintf(stderr, "Error occored while reading '%s", fnin); perror("'"); fclose(fout); remove(fnout); exit(1); } } if (length_hi != 0xffffffffL) { v2_start = header_length + length * 4; if (fseek(fin, length * 4, SEEK_CUR)) { fprintf(stderr, "Error occored while reading '%s", fnin); perror("'"); fclose(fout); remove(fnout); exit(1); } while (fgeti32(fin) != EBS_FINAL_TAG) { attribute_length = fgeti32(fin); if (fseek(fin, attribute_length * 4, SEEK_CUR)) { fprintf(stderr, "Error occored while reading '%s", fnin); perror("'"); fclose(fout); remove(fnout); exit(1); } } } } /* end of if (noinhead) ... */ /* write EBS file */ if (output_header) { /* fixed header */ fputi32(EBS_MAGIC1, fout); /* magic code */ fputi32(EBS_MAGIC2, fout); fputi32(outcode, fout); /* encoding ID */ fputi32(channels, fout); /* number of channels */ fputi32(samples_hi, fout); /* might be overwritten later */ fputi32(samples, fout); fputi32(0xffffffffL, fout); fputi32(0xffffffffL, fout); /* variable header, part 1 */ header_outlength = 32 + 4; if (input_header) { if (vhead == 'f' || vhead == 'd') { fseek(fin, 32, SEEK_SET); header_outlength += fvhcopy(noignore); if (v2_start > 0 && vhead == 'f') { fseek(fin, v2_start, SEEK_SET); header_outlength += fvhcopy(noignore); } } } fputi32(EBS_FINAL_TAG, fout); } /* data part */ old_samples = samples; newlength = dataconv(fin, fout, incode, outcode, header_length, header_outlength, channels, &samples, NULL); if (newlength < 0) { switch (newlength) { case -1: fprintf(stderr, "Error occored while reading '%s", fnin); if (feof(fin)) fprintf(stderr, "': unexpected end of file\n"); else perror("'"); break; case -2: fprintf(stderr, "Error occored while writing '%s", fnout); if (feof(fout)) fprintf(stderr, "': unexpected end of file\n"); else perror("'"); break; case -3: fprintf(stderr, "Not enought memory available!"); break; default: fprintf(stderr, "Unknow return code from dataconv() (%ld)!", newlength); break; } fclose(fout); remove(fnout); exit(1); } if (old_samples != 0xffffffffL && samples != old_samples) fprintf(stderr, "Warning: sample number in fixed header of '%s' " "was %ld samples too high!\n", fnin, old_samples - samples); /* variable header, part 2 */ if (input_header && output_header && (vhead == 'b' || (vhead == 'd' && length_hi != 0xffffffffL))) { while ((newlength & 3) != 0) { fputc(0, fout); newlength++; } if (vhead == 'b') { fseek(fin, 32, SEEK_SET); fvhcopy(noignore); } if (v2_start > 0) { fseek(fin, v2_start, SEEK_SET); fvhcopy(noignore); } fputi32(EBS_FINAL_TAG, fout); fseek(fout, 24, SEEK_SET); fputi32(0, fout); fputi32(newlength >> 2, fout); } if (output_header) { /* Update number of samples */ fseek(fout, 16, SEEK_SET); fputi32(0, fout); fputi32(samples, fout); } if (fclose(fout) || ferror(fout)) { fprintf(stderr, "Error while writing to '%s", fnout); perror("'"); remove(fnout); exit(1); } if (fclose(fin) || ferror(fin)) { fprintf(stderr, "Error while reading '%s", fnin); perror("'"); remove(fnout); exit(1); } if (!secondout) { if (remove(fnin)) { fprintf(stderr, "Can't unlink '%s", fnin); perror("'"); exit(1); } if (rename(fnout, fnin)) { fprintf(stderr, "Can't rename '%s' to '%s", fnout, fnin); perror("'"); exit(1); } } return(0); }