home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / amiga / midi / obrst103.lha / OberSuite-1.03 / SourceCode / obextract.c < prev    next >
C/C++ Source or Header  |  1993-01-23  |  12KB  |  516 lines

  1. /**************************************************************************
  2. * obextract.c:    The main program for ObExtract.
  3. *        Extract patch data from a 100-patch dump file and either
  4. *         store it in individual files, or send it via MIDI to the
  5. *         Oberheim synth.
  6. *        A part of OberSuite for the Commodore Amiga.
  7. *
  8. * Author:    Daniel Barrett, barrett@cs.umass.edu.
  9. * Version:    1.0.
  10. * Copyright:    None!  This program is in the Public Domain.
  11. *        Please share it with others.
  12. ***************************************************************************/
  13.  
  14. #include "decl.h"
  15. #include "obextract.h"
  16.  
  17. char *version = "$VER: ObExtract "  VERSION " " VERDATE;
  18.  
  19.  
  20. /***************************************************************************
  21. * The main program.
  22. ***************************************************************************/
  23.  
  24. main(argc, argv)
  25. int argc; char *argv[];
  26. {
  27.     int fileArg;        /* Argument position of the filename. */
  28.  
  29.    /* We use a bit vector to "check off" the numbers of the patches
  30.     * that we will be extracting. */
  31.  
  32.     BITS numbers[BITFIELD_LENGTH];
  33.  
  34.     Enable_Abort = 0;            /* Disable ^C aborts. */
  35.     strcpy(programName, BaseName(argv[0]));    /* Global variable. */
  36.     thePrintStyle    = VERBOSE;        /* Global variable. */
  37.     theDestination    = DEST_NONE;        /* Global variable. */
  38.     overwriteAll    = FALSE;        /* Global variable. */
  39.     useTheRealNames    = FALSE;        /* Global variable. */
  40.  
  41.     if (argc == 1)
  42.     {
  43.         ShortUsageMsg();
  44.         BegForUsage();
  45.     }
  46.     else if  ((argc == 2) && (!strcmp(argv[1], "?")))
  47.         DetailedUsage();
  48.     else if (!HandleOptions(argc, argv))
  49.         BegForUsage();
  50.     else if (optind >= argc-1)
  51.         ErrorMsg(ERROR_NUMARGS);
  52.     else if (! (fileArg = ReadRanges(numbers, optind, argv)) )
  53.         ErrorMsg(ERROR_PATCHNUM);
  54.     else if (fileArg != argc-1)
  55.         ErrorMsg(ERROR_NUMARGS);
  56.     else
  57.         ObExtract(numbers, argv[fileArg]);
  58. }
  59.  
  60.  
  61. /*
  62.  * Read patch data from the given file.  Extract the data for the patches
  63.  * specified in the bit vector "numbers".
  64.  */
  65.  
  66. void ObExtract(BITS numbers[], char *filename)
  67. {
  68.     PATCHINFO pi;
  69.  
  70.     InitPatchInfo(&pi);
  71.     pi.source = PI_SOURCE_FILE;
  72.  
  73.     if (!LookAtFileSize(&pi, filename))
  74.         ;
  75.     else if (!AllocPatchInfo(&pi))
  76.         ErrorMsg(ERROR_MALLOC);
  77.     else if (!GetPatchFromFile(&pi, filename))
  78.         ErrorMsg(ERROR_GETFAILED);
  79.     else if (!FigureThingsOutFromHeader(&pi))
  80.         ;
  81.     else if (!DoTheExtract(&pi, numbers, filename))
  82.         ;
  83.     else
  84.         ;    /* Do nothing. */
  85.  
  86.     FreePatchInfo(&pi);
  87. }
  88.  
  89.     
  90. /*
  91.  * Given a PATCHINFO struct full of patch data, and a bit vector indicating
  92.  * which patches to use, extract the patch data.  If we are extracting to
  93.  * a file, the string "filename" helps us form unique file names for the
  94.  * extracted patches.
  95.  * Return TRUE on success (else FALSE).
  96.  */
  97.  
  98. BOOL DoTheExtract(PATCHINFO *pi, BITS numbers[], char *filename)
  99. {
  100.     int i;
  101.     long offset  = 0L;
  102.     BOOL success = TRUE;
  103.     long tooFar;
  104.     UBYTE low=FIRST_PATCH, high=LAST_PATCH;
  105.  
  106.    /* We need at least "tooFar" bytes of data to be present. */
  107.  
  108.     tooFar = pi->numPatches * pi->rightSize;
  109.  
  110.    /* Figure out the lowest-numbered and highest-numbered patch we are
  111.     * are extracting. */
  112.  
  113.     ComputeLowAndHigh(numbers, &low, &high);
  114.  
  115.    /* If we are extracting to MIDI, turn on the serial stuff. */
  116.  
  117.     if ((theDestination & DEST_SYNTH) && !SerialSetup())
  118.         return(FALSE);
  119.  
  120.    /* For each patch that we want to extract, compute its offset in the
  121.     * patch data, extract the data, and send it on its way. */
  122.  
  123.     for (i=low; (i<=high) && success; i++)
  124.     {
  125.         if (CtrlcCheck())
  126.         {
  127.             ErrorMsg(ERROR_CTRLC);
  128.             success = FALSE;
  129.             break;
  130.         }
  131.  
  132.         offset = i * pi->rightSize;
  133.         if (offset >= tooFar)
  134.         {
  135.             ErrorMsg(ERROR_DATATOOSMALL);
  136.             break;
  137.         }
  138.         else if (BitOn(numbers, i) && VerifyPatch(pi, offset))
  139.         {
  140.                 if (theDestination & DEST_SYNTH)
  141.                 success &= ExtractOneToSynth(pi, offset);
  142.             if (theDestination & DEST_FILE)
  143.                 success &= ExtractOneToFile(pi, offset,
  144.                                 filename);
  145.         }
  146.     }
  147.  
  148.     if (theDestination & DEST_SYNTH)
  149.         SerialShutdown();
  150.  
  151.     return(success && (offset < tooFar));
  152. }
  153.  
  154.     
  155. /*
  156.  * Look at the bit vector "numbers" and find the lowest and highest
  157.  * numbered bits that are turned on.  Store their values in "low"
  158.  * and "high", respectively.
  159.  *
  160.  * We are GUARANTEED that before entering this function, "numbers" has
  161.  * at least 1 bit turned on between its FIRST_PATCH'th and LAST_PATCH'th
  162.  * bits (inclusive)... thanks to ReadRanges().
  163.  */
  164.  
  165. void ComputeLowAndHigh(BITS numbers[], UBYTE *low, UBYTE *high)
  166. {
  167.     UBYTE i;
  168.  
  169.    /* Find the lowest numbered bit by scanning the bit vector from lowest
  170.     * to highest bits until we find a bit that is turned on.
  171.     * We initialize low to LAST_PATCH so we have a valid answer no matter
  172.     * what. */
  173.  
  174.     *low = LAST_PATCH;
  175.     for (i=FIRST_PATCH;  i <= LAST_PATCH;  i++)
  176.     {
  177.         if (BitOn(numbers, i))
  178.         {
  179.             (*low) = i;
  180.             break;
  181.         }
  182.     }
  183.  
  184.    /*
  185.     * Find the highest patch number by scanning the bit vector from
  186.     * the highest bit down to "low" (calculated above), looking for a
  187.     * bit that is turned on.
  188.     */
  189.  
  190.     (*high) = (*low);
  191.     for (i=LAST_PATCH;  i > (*low);  i--)
  192.     {
  193.         if (BitOn(numbers, i))
  194.         {
  195.             (*high) = i;
  196.             break;
  197.         }
  198.     }
  199. }
  200.     
  201. /***************************************************************************
  202. * Extracting patches to FILES.
  203. ***************************************************************************/
  204.     
  205. BOOL ExtractOneToFile(PATCHINFO *pi, long offset, char *infile)
  206. {
  207.     PATCHINFO localPI;
  208.     char outfile[BUFSIZ];
  209.     char *baseName = NULL;
  210.     BOOL success = TRUE;
  211.  
  212.    /* Extract the base name of the input file, minus leading directories. */
  213.  
  214.     if ((baseName = BaseName(infile)) == NULL)
  215.         return(FALSE);
  216.  
  217.    /* Create the name of the output file. */
  218.  
  219.     MakePatchFileName(outfile, baseName, pi, offset);
  220.     if (OUTPUT_ALLOWED)
  221.         PrintPatchInfo(pi, offset);
  222.  
  223.    /* Make sure that the file, if it exists, may safely be overwritten. */
  224.  
  225.     if (!overwriteAll && DontOverwriteExistingFile(outfile))
  226.         ;
  227.  
  228.     else
  229.     {
  230.         if (CtrlcCheck())
  231.         {
  232.             ErrorMsg(ERROR_CTRLC);
  233.             return(FALSE);
  234.         }
  235.  
  236.         localPI = (*pi);
  237.         localPI.data = pi->data + offset;
  238.         localPI.numPatches = 1;
  239.  
  240.         if (success = PutPatchToFile(&localPI, outfile))
  241.         {
  242.             if (OUTPUT_ALLOWED)
  243.                 printf("  --> %s\n", outfile);
  244.         }
  245.         if (OUTPUT_ALLOWED)
  246.             putchar('\n');
  247.     }
  248.  
  249.     if (CtrlcCheck())
  250.     {
  251.         ErrorMsg(ERROR_CTRLC);
  252.         return(FALSE);
  253.     }
  254.     else
  255.         return(success);
  256. }
  257.  
  258.  
  259. /*
  260.  * Construct the output file name "outfile".  Its format is
  261.  *
  262.  *    orig  +  '.'  +  <'S' or 'M'>  +  <2-digit patch number>
  263.  * 
  264.  * unless useTheRealNames is true.  In that case, we form the name as
  265.  *
  266.  *     <real patch name>  +  '.'  +  <'S' or 'M'>  +  <2-digit patch number>
  267.  */
  268.     
  269. void MakePatchFileName(char *outfile, char *orig, PATCHINFO *pi, long offset)
  270. {
  271.     UBYTE patchNum, patchType;
  272.  
  273.     patchType = pi->data[offset + BYTE_PATCHTYPE];
  274.         patchNum  = pi->data[offset + BYTE_PATCHNUMBER];
  275.  
  276.     sprintf(outfile, "%s.%c%02d",
  277.         useTheRealNames ? PatchnameToFilename(pi, offset) : orig,
  278.         (patchType == MODE_SINGLE) ? LETTER_SINGLE : LETTER_MULTI,
  279.         patchNum);
  280. }
  281.  
  282.  
  283. /*
  284.  * Find the true patch name in pi->data.  Convert "troublesome" characters
  285.  * into DEFAULT_FILENAME_CHAR, and strip trailing blanks.  Return the
  286.  * resulting string in a static buffer.
  287.  */
  288.  
  289. char *PatchnameToFilename(PATCHINFO *pi, long offset)
  290. {
  291.     static char filename[NAME_LENGTH+1];
  292.     int i;
  293.     UBYTE *here    = pi->data + offset + pi->nameOffset;
  294.  
  295.    /* Transfer patch name to filename, eliminating non-alphanumeric chars. */
  296.  
  297.     for (i=0; i<NAME_LENGTH; i++)
  298.     {
  299.             if (isalnum(*here)  ||  ((*here) == ' '))
  300.             filename[i] = tolower(*here);
  301.         else
  302.             filename[i] = DEFAULT_FILENAME_CHAR;
  303.  
  304.         here += 2;
  305.     }
  306.  
  307.     filename[NAME_LENGTH] = '\0';
  308.  
  309.    /* 
  310.     * Remove trailing blanks, and convert other blanks to 
  311.     * DEFAULT_FILENAME_CHAR.
  312.     */
  313.     i = NAME_LENGTH-1;
  314.     while ((i >= 0)  &&  (filename[i] == ' '))
  315.     {
  316.         filename[i--] = '\0';
  317.     }
  318.     while (i >= 0)
  319.     {
  320.         if (filename[i] == ' ')
  321.             filename[i] = DEFAULT_FILENAME_CHAR;
  322.         --i;
  323.     }
  324.  
  325.    /* If filename is now completely empty, use a default name. */
  326.  
  327.     if (filename[0] == '\0')
  328.         strcpy(filename, DEFAULT_PATCH_NAME);
  329.  
  330.     return(filename);
  331. }
  332.  
  333.  
  334. /***************************************************************************
  335. * Extracting patches to MIDI (the synth).
  336. ***************************************************************************/
  337.  
  338. BOOL ExtractOneToSynth(PATCHINFO *pi, long offset)
  339. {
  340.     PATCHINFO localPI;
  341.  
  342.     localPI = (*pi);
  343.     localPI.data = pi->data + offset;
  344.     localPI.numPatches = 1;
  345.  
  346.     if (!FigureThingsOutFromHeader(&localPI))
  347.         return(FALSE);
  348.     else
  349.         return(RepeatedlySend(&localPI));
  350. }
  351.  
  352.     
  353. /***************************************************************************
  354. * Handle the command-line options.
  355. ***************************************************************************/
  356.  
  357. BOOL HandleOptions(int argc, char *argv[])
  358. {
  359.     int c;            /* The character to be read. */
  360.     short printed = 0;  /* How many "print" options have been chosen? */
  361.     BOOL success  = TRUE;
  362.  
  363.     while ((c = getopt(argc, argv, options)) != EOF)
  364.     {
  365.         switch (c)
  366.         {
  367.             case OPT_DEADQUIET:
  368.                 thePrintStyle = DEADQUIET;
  369.                 printed++;
  370.                 break;
  371.             case OPT_SILENT:
  372.                 thePrintStyle = SILENT;
  373.                 printed++;
  374.                 break;
  375.             case OPT_VERBOSE:
  376.                 thePrintStyle = VERBOSE;
  377.                 printed++;
  378.                 break;
  379.             case OPT_SYNTH:
  380.                 theDestination |= DEST_SYNTH;
  381.                 break;
  382.             case OPT_FILE:
  383.                 theDestination |= DEST_FILE;
  384.                 break;
  385.             case OPT_OVERWRITE:
  386.                 overwriteAll = TRUE;
  387.                 break;
  388.             case OPT_USEREALNAMES:
  389.                 useTheRealNames = TRUE;
  390.                 break;
  391.             default:
  392.                 return(FALSE);
  393.         }
  394.     }
  395.  
  396.     if (printed > 1)          /* User chose > 1 print option. */
  397.     {
  398.         ErrorMsg(ERROR_TWOPRINTS);
  399.         success = FALSE;
  400.     }
  401.  
  402.     if (!theDestination)          /* User didn't choose a dest. */
  403.     {
  404.         ErrorMsg(ERROR_NODEST);
  405.         success = FALSE;
  406.     }
  407.  
  408.     if (useTheRealNames && !(theDestination & DEST_FILE))
  409.     {
  410.         ErrorMsg(ERROR_REALNAMESFILEONLY);
  411.         success = FALSE;
  412.     }
  413.  
  414.     if (overwriteAll && !(theDestination & DEST_FILE))
  415.     {
  416.         ErrorMsg(ERROR_OVERWRITEFILEONLY);
  417.         success = FALSE;
  418.     }
  419.     
  420.     return(success);
  421. }
  422.  
  423.  
  424. /*
  425.  * Given a list of ranges beginning at argv[firstArg], calculate their
  426.  *  contents and turn on the corresponding bits in the vector "numbers".
  427.  * Return the position of the next unprocessed argument.
  428.  * (Presumably it will be our input filename.)
  429.  */
  430.  
  431. int ReadRanges(BITS numbers[], int firstArg, char *argv[])
  432. {
  433.     int upper, lower;
  434.  
  435.     ClearBitfield(numbers);
  436.     while (argv[firstArg] && isdigit(argv[firstArg][0]))
  437.     {
  438.         if (MakeRange(argv[firstArg], &upper, &lower))
  439.         {
  440.             if (!AddToRange(numbers, upper, lower))
  441.                 return(0);
  442.             firstArg++;
  443.         }
  444.         else
  445.             return(0);
  446.     }
  447.  
  448.     return(firstArg);
  449. }
  450.  
  451.     
  452. /***************************************************************************
  453. * Usage information.
  454. ***************************************************************************/
  455.     
  456. void ShortUsageMsg(void)
  457. {
  458.     if (!ERR_OUTPUT_ALLOWED)    return;
  459.  
  460.     fprintf(stderr,
  461.         "Usage: %s [options] PATCH [PATCH...] filename\n",
  462.         programName);
  463. }
  464.  
  465.  
  466. void UsageMsg(void)
  467. {
  468.     if (!ERR_OUTPUT_ALLOWED)    return;
  469.  
  470.     fprintf(stderr,
  471.         "%s extracts individual patches from a %d-patch file.\n",
  472.         programName, NUM_PATCHES);
  473.  
  474.     ShortUsageMsg();
  475.  
  476.     fprintf(stderr, "\nLegal options are:\n");
  477.     fprintf(stderr,
  478.         "\t-%c:\tExtracted patches are put into individual files.\n",
  479.         OPT_FILE);
  480.     fprintf(stderr,
  481.         "\t-%c:\tExtracted patches are sent to the Oberheim.\n",
  482.         OPT_SYNTH);
  483.     fprintf(stderr,
  484.         "\t-%c:\tOverwrite existing files without asking permission."
  485.         "(-%c only)\n",
  486.         OPT_OVERWRITE, OPT_FILE);
  487.     fprintf(stderr, 
  488.         "\t-%c:\tUse actual patch names for file names (-%c only).\n",
  489.         OPT_USEREALNAMES, OPT_FILE);
  490.     fprintf(stderr, "\t-%c:\tQuiet output; error messages only.\n",
  491.         OPT_SILENT);
  492.     fprintf(stderr, "\t-%c:\tNo output; not even error messages.\n",
  493.         OPT_DEADQUIET);
  494.     fprintf(stderr, "\t-%c:\tLong output (DEFAULT).\n", OPT_VERBOSE);
  495.     fprintf(stderr, "You MUST specify -%c or -%c (or both).\n",
  496.         OPT_FILE, OPT_SYNTH);
  497.  
  498.     fprintf(stderr, "\nEach \"PATCH\" can be one patch number,");
  499.     fprintf(stderr, " or a range like 3-18 or 64-60.\n");
  500.     fprintf(stderr,
  501.         "Ranges may overlap and be increasing OR decreasing.\n");
  502.  
  503.     fprintf(stderr, "Examples:\n");
  504.     fprintf(stderr, "\t%s -%c 2 5 19-27 HundredPatchFile\n",
  505.             programName, OPT_FILE);
  506.     fprintf(stderr, "\t%s -%c%c 81 85-21 HundredPatchFile\n",
  507.             programName, OPT_SYNTH, OPT_SILENT);
  508. }
  509.  
  510.  
  511. char *Version(void)
  512. {
  513.     static char v[] = VERSION;
  514.     return(v);
  515. }
  516.