home *** CD-ROM | disk | FTP | other *** search
/ Quake 'em / QUAKEEM.BIN / quake / programs / qube / qube.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-03-07  |  11.8 KB  |  371 lines

  1. /***
  2. ****  QuBE Version 0.2a
  3. ****
  4. ****  Cheap Quake Binary Editor
  5. ****
  6. ****  Copyright 1996 by Sean Werkema
  7. ****
  8. ****  Main startup file, command line parsing, etc. --- get the ball rolling.
  9. ****
  10. ****  Compilation notes...  QUBE_MSDOS and QUBE_UNIX have special meaning
  11. ****  in this thing.  Each indicates the appropriate operating system.    More
  12. ****  can be added as necessary later.
  13. ****
  14. ****  Another nice addition would be QUBE_BIG and QUBE_LITTLE for specifying
  15. ****  little-endian vs. big-endian...  Anyone willing to code this mess?
  16. ****
  17. ****  I make no guarantees on the quality of this code.  You can freely use
  18. ****  it, modify it, tweak it, whatever you want, just as long as you obey
  19. ****  one basic command:  Whatever you create, don't call it QuBE.  QuBE is
  20. ****  my name for this morass of code, and it stays mine.  Other than that
  21. ****  you can do anything you damn well please.
  22. ****
  23. ****  This code is copyrighted only so that nobody else will copyright it;
  24. ****  I wrote it, and I don't want to be restricted from my own code.
  25. ****
  26. ****  Contributors:
  27. ****    Sean Werkema            Started QuBE, wrote 99% of it so far.
  28. ****
  29. ****  I am deeply indebted to whoever it was that originally posted the .PAK
  30. ****  extracter on the Net, because that gave me enough clues to start this.
  31. ****  The .PAK extraction routines are based roughly on the original .PAK
  32. ****  posting, with some new error checking and reformatting.  Whoever wrote
  33. ****  the .PAK thing, would you please contact me so I can throw in your name
  34. ****  as a contributor?  Thanks.
  35. ****
  36. ****  Enough comments.    Let's do this thing.
  37. ***/
  38.  
  39. #include "qube.h"
  40. #include <errno.h>
  41. #include <stdarg.h>
  42.  
  43. #include "image.h"
  44. #include "gfx.h"
  45. #include "entities.h"
  46. #include "tree.h"
  47. #include "pak.h"
  48. #include "vertex.h"
  49. #include "edge.h"
  50.  
  51. /* Automatically casted name of an Action function */
  52.  
  53. #define A(n) ((void (*)(int argnum, char **argv))(n))
  54.  
  55. /* The input file in all its glory */
  56.  
  57. struct headertag header;
  58. FILE *fi;
  59. int filenamearg;
  60. int justcreated;
  61. int verbose;
  62.  
  63. /* This is an action.  It's what to do with a command-line switch */
  64.  
  65. typedef struct {
  66.     char *option;
  67.     long int count;
  68.     void (*action)(int argnum, char **argv);
  69.         char *cmdline;
  70.     char *name;
  71.     char *info;
  72. } Action;
  73.  
  74. typedef struct {
  75.     Action *act;
  76.     int arg;
  77. } CommandArgument;
  78.  
  79. /* Everything that can be should be kept static, especially private
  80.    functions and data, like the switch-list and its manipulators.  This
  81.    isn't OOP code, but we can try to make it close.  */
  82.  
  83. static void ShowFormat(void);
  84. static int switchcmp(char *option, char *string);
  85. static void Verbosity(void);
  86.  
  87. static CommandArgument CommandArg[256];
  88. static int CommandCount;
  89.  
  90. /* Command line args go here.  This is where everything starts.  */
  91.  
  92. static Action ActionList[] = {
  93.  
  94. { "-b",  0,     A(TreeList),    "[-b]",                 "BSP",          "Display the BSP tree in a .BSP file"                           },
  95. { "-dl", 0,     A(EdgeList),    "[-dl]",                "EdgeList",     "Display the list of edges"                                     },
  96. { "-el", 0,     A(EntList),     "[-el]",                "EntList",      "Display entities in a .BSP file as a printed list"             },
  97. { "-er", 1,     A(EntReplace),  "[-er filename]",       "EntReplace",   "Replace *all* entities in a .BSP file from a text file"        },
  98. { "-ex", 1,     A(EntXtract),   "[-ex filename]",       "EntXtract",    "Extract entities in a .BSP file to a text file"                },
  99. { "-f",  0,     A(ShowHeader),  "[-f]",                 "FileHeader",   "Display BSP file header information"                           },
  100. { "-g",  0,     A(DoGraphics),  "[-g]",                 "Graph",        "Display the level in a .BSP file (SVGA/X-Win only)"            },
  101. { "-h",  0,     A(ShowFormat),  "[-h]",                 "Help",         "You're looking at it, buddy"                                   },
  102. { "-ka", 1,     A(PakAdd),      "[-ka filename]",       "PakAdd",       "Create/add/replace file(s) into a .PAK file"                   },
  103. { "-kd", 1,     A(PakDelete),   "[-kd filename]",       "PakDelete",    "Delete file(s) from a .PAK file"                               },
  104. { "-kl", 0,     A(PakList),     "[-kl]",                "PakList",      "List all the files stored in a .PAK file"                      },
  105. { "-kx", 1,     A(PakXtract2),  "[-kx filename]",       "PakXtract",    "Extract one or more files from a .PAK file"                    },
  106. { "-kX", 0,     A(PakXtract),   "[-kX]",                "PakXtract",    "Extract a .PAK file into all its constituent files"            },
  107. { "-pa", 1,     A(NULL),        "[-pa filename]",       "PicAdd",       "Add a picture to a .BSP file from a .BMP file (not yet)"       },
  108. { "-pd", 1,     A(NULL),        "[-pd filename]",       "PicDelete",    "Delete a picture from a .BSP file (not yet)"                   },
  109. { "-pl", 0,     A(PicList),     "[-pl]",                "PicList",      "List all the pictures stored in a .BSP file"                   },
  110. { "-px", 2,     A(PicXtract),   "[-px filename size]",  "PicXtract",    "Extract a picture from a .BSP file into a .BMP file"           },
  111. { "-pX", 0,     A(PicXtract2),  "[-pX]",                "PicXtract",    "Extract all pictures from a .BSP file"                         },
  112. { "-v",  0,     A(Verbosity),   "[-v]",                 "Verbose",      "Describe everything while doing it (good for debugging)"       },
  113. { "-xl", 0,     A(VertexList),  "[-xl]",                "VertexList",   "Display the list of vertices"                                  },
  114. { "-!",  1,     A(XtractAll),   "[-! filename]",        "XtractAll",    "Extract *ALL* data in a .BSP file to separate files"           },
  115. { NULL,  0,     A(NULL),        NULL,                   NULL,           NULL                                                            },
  116.  
  117. };
  118.  
  119. /*
  120. **  Main.
  121. */
  122.  
  123. int main(int argc, char **argv)
  124. {
  125.     int i, j;
  126.     int filenum = 0;
  127.     int createok = 0;
  128.  
  129.     verbose = 0;
  130.         justcreated = 0;
  131.     fi = NULL;
  132.  
  133.     fprintf(stderr, "QuBE: Version 0.2a - 3/8/1996 - Freeware - Sean Werkema\n");
  134.  
  135.     /* Start parsing in the command line, and open a file if one exists */
  136.  
  137.         for (i = 1, CommandCount = 0; i < argc; i++) {
  138.                 for (j = 0; ActionList[j].option != NULL; j++) {
  139.                         if (switchcmp(ActionList[j].option, argv[i])) {
  140.                 CommandArg[CommandCount].arg = i;
  141.                 CommandArg[CommandCount++].act = ActionList + j;
  142.                 if (ActionList[j].action == A(ShowFormat)) ShowFormat();
  143.                 if (ActionList[j].action == A(PakAdd)) createok = 1;
  144.                                 i += ActionList[j].count;
  145.                                 break;
  146.                         }
  147.                 }
  148.         if (ActionList[j].option == NULL) {
  149.             if (filenum != 0)
  150.                 Error("Hey, that's too much stuff to do - try \"qube -h\".");
  151.             filenum = i;
  152.                 }
  153.         }
  154.  
  155.     if (filenum) {
  156.         if ((fi = fopen(argv[filenum], "rb")) == NULL) {
  157.             if (!createok) Error(strerror(errno));
  158.             fi = fopen(argv[filenum], "wb");
  159.             fclose(fi);
  160.             fi = fopen(argv[filenum], "r+b");
  161.                         justcreated = 1;
  162.                 }
  163.         else fread(&header, 4, sizeof(header), fi);
  164.         setvbuf(fi, NULL, _IOFBF, 32768);
  165.                 filenamearg = filenum;
  166.     }
  167.     else Error("Sorry, Mac, I don't know what to do - try \"qube -h\".");
  168.  
  169.     /* Start acting on the stuff that was found on the command line */
  170.  
  171.     for (i = 0; i < CommandCount; i++) {
  172.         if (CommandArg[i].act->action != NULL) {
  173.             void (*action)(int argnum, char **argv) = CommandArg[i].act->action;
  174.  
  175.                         (*action)(CommandArg[i].arg, argv);
  176.         }
  177.                 else Error("Sorry, but this feature hasn't been written yet.");
  178.         }
  179.  
  180.     if (fi != NULL) fclose(fi);
  181.  
  182.     return(1);
  183. }
  184.  
  185. /*
  186. **  Verbosity.    Turn on the verbose flag.
  187. */
  188.  
  189. static void Verbosity(void)
  190. {
  191.     verbose = 1;
  192. }
  193.  
  194. /*
  195. **  Error.  Display an error and shut down.  Should use vsprintf to make
  196. **    everything nice, but that's a bit of effort for now.
  197. */
  198.  
  199. void Error(char *format, ...)
  200. {
  201.     char temp[1024];
  202.     va_list arg_ptr;
  203.     va_start(arg_ptr, format);
  204.     vsprintf(temp, format, arg_ptr);
  205.  
  206.         if (fi != NULL) fclose(fi);
  207.  
  208.     fprintf(stderr, "QuBE: %s\n", temp);
  209.  
  210. #ifdef QUBE_UNIX
  211.     /* One extra CR is needed to make things look right in UNIX */
  212.         fprintf(stderr, "\n");  
  213. #endif
  214.  
  215.     exit(0);
  216. }
  217.  
  218. /*
  219. **  ShowFormat.  Extacts the format from the ActionList, displays it, and exits.
  220. */
  221.  
  222. static void ShowFormat(void)
  223. {
  224.     int line = 0, column = 0;
  225.     int i;
  226.     int lc = 2;
  227.  
  228.     fprintf(stderr, "Display and change things in Quake BSP/PAK files.\n");
  229.     lc++;
  230.  
  231.     /* Display the top command line */
  232.  
  233.     for (i = 0; ActionList[i].option != NULL; i++) {
  234.         if (column + strlen(ActionList[i].cmdline) > 78) column = 0;
  235.                 if (column == 0) {
  236.             if (line++ == 0) fprintf(stderr, "\nqube");
  237.             else         fprintf(stderr, "\n    ");
  238.             column = 4;
  239.             lc++;
  240.                 }
  241.         fprintf(stderr, " %s", ActionList[i].cmdline);
  242.         column += strlen(ActionList[i].cmdline) + 1;
  243.         }
  244.  
  245.     /* Remind them they need to add a filename */
  246.  
  247.     if (column == 0 || column > 70) {
  248.         if (line++ == 0) fprintf(stderr, "\nQuBE");
  249.         else         fprintf(stderr, "\n    ");
  250.         lc++;
  251.         }
  252.     fprintf(stderr, " filename\n\n");
  253.  
  254.     /* Display the command line options, long form */
  255.  
  256.     for (i = 0; ActionList[i].option != NULL; i++) {
  257.         if (strlen(ActionList[i].name) != 0) {
  258.             fprintf(stderr, "  %-6s%-15s%s\n", ActionList[i].option, ActionList[i].name, ActionList[i].info);
  259.             lc++;
  260.             if (lc == 23) {
  261.                 fprintf(stderr, "<More>");
  262. #ifdef QUBE_MSDOS
  263.                 ReadKeyScan();
  264.                 fprintf(stderr, "\r");
  265. #endif
  266. #ifdef QUBE_UNIX
  267.                 scanf("%c", &line);
  268. #endif
  269.                                 lc = 1;
  270.                         }
  271.                 }
  272.     }
  273.  
  274. #ifdef QUBE_UNIX
  275.     /* Again, one extra CR is needed to make things look right in UNIX */
  276.         fprintf(stderr, "\n");  
  277. #endif
  278.  
  279.     exit(0);
  280. }
  281.  
  282. /*
  283. **  switchcmp.    Behaves kind of like strncmp, only faster and friendlier.
  284. */
  285.  
  286. static int switchcmp(char *option, char *string)
  287. {
  288.     if (*option == '\0') return(0);
  289.  
  290.     while (*option && *option == *string) {
  291.         option++;
  292.         string++;
  293.     }
  294.  
  295.     if (*option == '\0') return(1);
  296.     else return(0);
  297. }
  298.  
  299. /*
  300. **  MatchName.    Wildcard matcher.  Now handles the wildcards fully, in
  301. **    pathname format (with proper / interpretation).
  302. **
  303. **    Note that this is rather loosely derived from the fnmatch()
  304. **    function from the GNU C library, which means that at least this
  305. **    part of the code can have no restrictions on distribution.
  306. **
  307. **    However, since the rest of the code can't have any restrictions
  308. **    either (by my choice), I guess that's a moot point.
  309. */
  310.  
  311. int MatchName(char *expr, char *string)
  312. {
  313.     char c;
  314.  
  315.     while ((c = *expr++) != '\0') {
  316.         switch (c) {
  317.         case '?':
  318.             if (*string == '\0') return(0);
  319.                         break;
  320.         case '*':
  321.             while ((c = *expr++) == '?' || c == '*') {
  322.                 if (c == '?' && *string == '\0') return(0);
  323.                 string++;
  324.             }
  325.                         if (c == '\0') return(1);
  326.             expr--;
  327.             while (*string != '\0') {
  328.                 if ((*string == c) && MatchName(expr, string)) return(1);
  329.                                 string++;
  330.             }
  331.                         return(0);
  332.         default:
  333.             if (c != *string) return(0);
  334.             break;
  335.         }
  336.         string++;
  337.         }
  338.  
  339.     if (*string == '\0') return(1);
  340.     else return(0);
  341. }
  342.  
  343. void *Qmalloc(long int size)
  344. {
  345.     void *temp = malloc(size);
  346.  
  347.     if (temp == NULL) {
  348.         fprintf(stderr, "Unable to allocate enough memory.  %ld bytes not available.\n", size);
  349.                 exit(0);
  350.         }
  351.  
  352.     return(temp);
  353. }
  354.  
  355. void *Qrealloc(void *buffer, long int size)
  356. {
  357.     void *temp = realloc(buffer, size);
  358.  
  359.     if (temp == NULL) {
  360.         fprintf(stderr, "Unable to allocate enough memory.  %ld bytes not available.\n", size);
  361.         exit(0);
  362.         }
  363.  
  364.         return(temp);
  365. }
  366.  
  367. void Qfree(void *buffer)
  368. {
  369.     free(buffer);
  370. }
  371.