#include "qcc.h" #include #include #if defined(__linux__) || defined(__unix__) #include #endif /* ============== LoadFile ============== */ static void *QCC_ReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile) //unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, size_t *sz) { size_t len; FILE *f; char *buffer; f = fopen(fname, "rb"); if (!f) { if (out_size) *out_size = 0; return NULL; } fseek(f, 0, SEEK_END); len = ftell(f); fseek(f, 0, SEEK_SET); if (buf_get) buffer = buf_get(buf_ctx, len+1); else buffer = malloc(len+1); ((char*)buffer)[len] = 0; if (len != fread(buffer, 1, len, f)) { if (!buf_get) free(buffer); buffer = NULL; } fclose(f); if (out_size) *out_size = len; return buffer; } static int PDECL QCC_FileSize (const char *fname) { long length; FILE *f; f = fopen(fname, "rb"); if (!f) return -1; fseek(f, 0, SEEK_END); length = ftell(f); fclose(f); return length; } static pbool PDECL QCC_WriteFile (const char *name, void *data, int len) { long length; FILE *f; f = fopen(name, "wb"); if (!f) return false; length = fwrite(data, 1, len, f); fclose(f); if (length != len) return false; return true; } #undef printf #undef Sys_Error static void PDECL Sys_Error(const char *text, ...) { va_list argptr; static char msg[2048]; va_start (argptr,text); QC_vsnprintf (msg,sizeof(msg)-1, text,argptr); va_end (argptr); QCC_Error(ERR_INTERNAL, "%s", msg); } static FILE *logfile; static int logprintf(const char *format, ...) { va_list argptr; static char string[1024]; va_start (argptr, format); #ifdef _WIN32 _vsnprintf (string,sizeof(string)-1, format,argptr); #else vsnprintf (string,sizeof(string), format,argptr); #endif va_end (argptr); fprintf(stderr, "%s", string); // fputs(string, stderr); if (logfile) fputs(string, logfile); return 0; } static size_t totalsize, filecount; static void QCC_FileList(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize) { totalsize += plainsize; filecount += 1; if (!method && compsize==plainsize) externs->Printf("%8u %s\n", (unsigned)plainsize, name); else externs->Printf("%8u %3u%% %s\n", (unsigned)plainsize, plainsize?(unsigned)((100*compsize)/plainsize):100u, name); } #include #ifdef __unix__ #include void QCC_Mkdir(const char *path) { char buf[MAX_OSPATH], *sl; if (!strchr(path, '/')) return; //no need to create anything memcpy(buf, path, MAX_OSPATH); while((sl=strrchr(buf, '/'))) { *sl = 0; mkdir(buf, 0777); } } #else void QCC_Mkdir(const char *path) { //unsupported. } #endif static char qcc_tolower(char c) { if (c >= 'A' && c <= 'Z') return c-'A'+'a'; return c; } int qcc_wildcmp(const char *wild, const char *string) { while (*string) { if (*wild == '*') { if (*string == '/' || *string == '\\') { //* terminates if we get a match on the char following it, or if its a \ or / char wild++; continue; } if (qcc_wildcmp(wild+1, string)) return true; string++; } else if ((qcc_tolower(*wild) == qcc_tolower(*string)) || (*wild == '?')) { //this char matches wild++; string++; } else { //failure return false; } } while (*wild == '*') { wild++; } return !*wild; } static const char *extractonly; static pbool extractonlyfound; static void QCC_FileExtract(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize) { if (extractonly) { const char *sl = strrchr(extractonly, '/'); if (sl && !sl[1]) { //trailing / - extract the entire dir. if (!strcmp(name, extractonly)) return; //ignore the dir itself... if (strncmp(name, extractonly, strlen(extractonly))) return; } else if (!qcc_wildcmp(extractonly, name)) return; //ignore it if its not the one we're going for. } extractonlyfound = true; externs->Printf("Extracting %s...", name); if (plainsize <= INT_MAX) { void *buffer = malloc(plainsize); if (buffer && QC_decode(progfuncs, compsize, plainsize, method, compdata, buffer)) { QCC_Mkdir(name); if (!QCC_WriteFile(name, buffer, plainsize)) externs->Printf(" write failure\n"); else externs->Printf(" done\n"); } else externs->Printf(" read failure\n"); free(buffer); } else externs->Printf(" too large\n"); } static void QCC_PR_PackagerMessage(void *userctx, const char *message, ...) { va_list argptr; char string[1024]; va_start (argptr,message); QC_vsnprintf (string,sizeof(string)-1,message,argptr); va_end (argptr); externs->Printf ("%s", string); } int main (int argc, const char **argv) { unsigned int i; pbool sucess; #if 0//def _WIN32 pbool writelog = true; //spew log files on windows. windows often closes the window as soon as the program ends making its output otherwise unreadable. #else pbool writelog = false; //other systems are sane. #endif int colours = 2; //auto progexterns_t ext; progfuncs_t funcs; progfuncs = &funcs; memset(&funcs, 0, sizeof(funcs)); funcs.funcs.parms = &ext; memset(&ext, 0, sizeof(progexterns_t)); funcs.funcs.parms->ReadFile = QCC_ReadFile; funcs.funcs.parms->FileSize = QCC_FileSize; funcs.funcs.parms->WriteFile = QCC_WriteFile; funcs.funcs.parms->Printf = logprintf; funcs.funcs.parms->Sys_Error = Sys_Error; if ((argc == 3 && !strcmp(argv[1], "-l")) || (argc >= 3 && !strcmp(argv[1], "-x"))) { size_t blobsize; void *blob = QCC_ReadFile(argv[2], NULL, NULL, &blobsize, false); if (!blob) { logprintf("Unable to read %s\n", argv[2]); return EXIT_FAILURE; } if (argc > 3) { for (i = 3; i < argc; i++) { extractonly = argv[i]; extractonlyfound = false; QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract); if (!extractonlyfound) externs->Printf("Unable to find file %s\n", extractonly); } extractonly = NULL; } else if (argv[1][1] == 'x') QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract); else { QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileList); externs->Printf("Total size %u bytes, %u files\n", (unsigned)totalsize, (unsigned)filecount); } free(blob); return EXIT_SUCCESS; } if (argc == 3 && (!strncmp(argv[1], "-z", 2) || !strcmp(argv[1], "-0") || !strcmp(argv[1], "-9"))) { //exe -0 foo.pk3dir enum pkgtype_e t; if (argv[1][1] == '9') t = PACKAGER_PK3; else if (argv[1][1] == '0') t = PACKAGER_PAK; //not really any difference but oh well else t = PACKAGER_PK3_SPANNED; if (Packager_CompressDir(argv[2], t, QCC_PR_PackagerMessage, NULL)) return EXIT_SUCCESS; else return EXIT_FAILURE; } for (i = 0; i < argc; i++) { if (!argv[i]) continue; if (!strcmp(argv[i], "-log")) writelog = true; else if (!strcmp(argv[i], "-nolog")) writelog = false; //arg consistency with ls else if (!strcmp(argv[i], "--color=always") || !strcmp(argv[i], "--color")) colours = 1; else if (!strcmp(argv[i], "--color=never")) colours = 0; else if (!strcmp(argv[i], "--color=auto")) colours = 2; } #if defined(__linux__) || defined(__unix__) if (colours == 2) colours = isatty(STDOUT_FILENO); if (colours) { //only use colours if its a tty, and not if we're redirected. col_none = "\e[0;m"; //reset to white col_error = "\e[0;31m"; //red col_symbol = "\e[0;32m"; //green col_warning = "\e[0;33m"; //yellow //col_ = "\e[0;34m"; //blue col_name = "\e[0;35m"; //magenta col_type = "\e[0;36m"; //cyan col_location = "\e[0;1;37m"; //bright white } #else (void)colours; #endif logfile = writelog?fopen("fteqcc.log", "wt"):false; if (logfile) { fputs("Args:", logfile); for (i = 0; i < argc; i++) { if (!argv[i]) continue; if (strchr(argv[i], ' ')) fprintf(logfile, " \"%s\"", argv[i]); else fprintf(logfile, " %s", argv[i]); } fprintf(logfile, "\n"); } sucess = CompileParams(&funcs, NULL, argc, argv); qccClearHunk(); if (logfile) fclose(logfile); #ifdef _WIN32 // fgetc(stdin); //wait for keypress #endif return sucess?EXIT_SUCCESS:EXIT_FAILURE; }