diff src/main.c @ 79:8f90d5addda9 v2.0

*: refactor... basically everything! The Win32 GUI version is now unicode-friendly. HOWEVER, ANSI still very much works. you can configure which version to use through `-DUNICODE=0/1` in CFLAGS. the CLI is also friendlier and uses a more sane interface as well. note: the command line flags (which were optional before) are now required. Unicode filenames will not work on Windows because Windows sucks.
author Paper <paper@paper.us.eu.org>
date Wed, 20 Mar 2024 17:06:26 -0400
parents fcd4b9fe957b
children c06dcab17923
line wrap: on
line diff
--- a/src/main.c	Sun Oct 01 03:03:29 2023 -0400
+++ b/src/main.c	Wed Mar 20 17:06:26 2024 -0400
@@ -1,160 +1,186 @@
 #include <stdio.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
+//#include <unistd.h>
 #include <getopt.h>
 #include <libgen.h>
-#include "../include/common.h"
-#ifdef _MSC_VER
-#define strdup(p) _strdup(p)
+
+#include "common.h"
+
+/* non-portable functions */
+static inline char* msvpvf_internal_strdup(const char* str) {
+	size_t len = strlen(str) + 1;
+
+	char* copy = malloc(len);
+	if (!copy)
+		return NULL;
+
+	memcpy(copy, str, len);
+	return copy;
+}
+
+/* source needs read permissions, target needs write permissions, both must be in binary mode */
+static inline int copy_file(FILE* source, FILE* target) {
+	char ch[4096];
+
+	while (!feof(source)) {
+		size_t b = fread(ch, 1, sizeof(ch), source);
+		if (b)
+			fwrite(ch, 1, b, target);
+	}
+
+	return 0;
+}
+
+static inline const char* type_to_string(enum types type) {
+	switch (type) {
+		case TYPES_VF: return "Movie Studio";
+		case TYPES_VEG: return "Vegas Pro";
+		case TYPES_UNKNOWN:
+		default: return "Unknown";
+	}
+}
+
+static inline const char* type_to_extension(enum types type) {
+	switch (type) {
+		case TYPES_VF: return "vf";
+		case TYPES_VEG:
+		case TYPES_UNKNOWN:
+		default: return "veg";
+	}
+}
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
 #endif
 
+static const char* help_text =
+	"msvpvf by Paper\n"
+	"usage: %s <input>... (arguments)\n"
+	"\n"
+	"arguments:\n"
+	"  -t, --type       one of [vf,veg] (required)\n"
+	"  -v, --version    version to convert to (required)\n"
+	"  -h, --help       show this screen (optional)\n";
+
 static struct option options_long[] = {
-	{"input", required_argument, NULL, 'i'},
-	{"output", required_argument, NULL, 'o'},
 	{"version", required_argument, NULL, 'v'},
 	{"type", required_argument, NULL, 't'},
 	{"help", 0, NULL, 'h'}
 };
 
-char* strremove(char* str, const char* sub) {
-    size_t len = strlen(sub);
-    if (len > 0) {
-        char* p = str;
-        while ((p = strstr(p, sub)) != NULL) {
-            memmove(p, p + len, strlen(p + len) + 1);
-        }
-    }
-    return str;
-}
+int main(int argc, char *argv[]) {
+	uint8_t version = 0;
+	enum types type = TYPES_UNKNOWN;
 
-int main(int argc, char *argv[]) {
-	int c, option_index = 0;
-	unsigned char magic[16];
-	FILE* outfile;
-	struct arguments {
-		char input[256];
-		char output[256];
-		int version;
-		char type[4];
-	} args;
-	strcpy(args.input, " ");
-	strcpy(args.output, " ");
-	args.version = -1;
-	strcpy(args.type, " ");
-
-	while ((c = getopt_long(argc, argv, "i:o:v:t:h", options_long, &option_index)) != -1)
-		switch(c) {
-			case 'i':
-				strncpy(args.input, optarg, sizeof(args.input)-1);  /* subtract 1 to make sure it's "null-safe" */
-				break;
-			case 'o':
-				strncpy(args.output, optarg, sizeof(args.output)-1);
-				break;
+	int c;
+	int option_index = 0;
+	while ((c = getopt_long(argc, argv, "v:t:h", options_long, &option_index)) != -1) {
+		/* option argument */
+		switch (c) {
 			case 'v':
-				args.version = abs(atoi(strdup(optarg)));  /* abs() for possible negative inputs */
+				version = atoi(optarg);
 				break;
 			case 't':
-				strncpy(args.type, optarg, sizeof(args.type)-1);
+				if (!strcmp(optarg, "vf")) {
+					type = TYPES_VF;
+				} else if (!strcmp(optarg, "veg")) {
+					type = TYPES_VEG;
+				} else {
+					fprintf(stderr, "[ERROR]: Received an invalid type parameter!\n");
+					printf(help_text, argv[0]);
+				}
+
 				break;
 			case 'h':
 			default:
-				printf("msvpvf by Paper\nusage: %s (-i/--input) infile [(-o/--output) outfile] (-v/--version) version (-t/--type) [vf, veg]\n", argv[0]);
-				return 0;
+				printf(help_text, argv[0]);
+				break;
 		}
-	if (argc == 1) {
-		printf("msvpvf by Paper\nusage: %s (-i/--input) infile [(-o/--output) outfile] (-v/--version) version (-t/--type) [vf, veg]\n", argv[0]);
-		return 0;
 	}
-	if (strcmp(args.input, " ") == 0) {
-		printf("Input file name?\n");
-		fflush(stdout);
-		fgets(args.input, sizeof(args.input)-1, stdin);
-		args.input[strcspn(args.input, "\r\n")] = 0;
-	}
-	if (access(args.input, F_OK) != 0) {
-		fprintf(stderr, "Input file \"%s\" doesn't exist! Exiting.", args.input);
+
+	if (argc <= optind) {
+		fprintf(stderr, "[ERROR]: Missing input file!\n");
+		printf(help_text, argv[0]);
 		return 1;
 	}
-	FILE* input_file = fopen(args.input, "r");
-	if (fgetc(input_file) == EOF) {
-		fprintf(stderr, "Input file \"%s\" is empty.", args.input);
-		fclose(input_file);
+
+	if (!version || !type) {
+		printf(help_text, (argc > 0) ? argv[0] : "msvpvf");
 		return 1;
 	}
-	fseek(input_file, 0x46, SEEK_SET);
-	printf("Input file version: %d\n", fgetc(input_file));
-	fseek(input_file, 0x18, SEEK_SET);
-	int file_version = fgetc(input_file);
-	printf("Input file type: ");
-	switch (file_version) {
-		case 0xEF:
-			printf("VEGAS Pro\n\n");
-			break;
-		case 0xF6:
-			printf("Movie Studio\n\n");
-			break;
-		default:
-			printf("Unknown\n\n");
-			break;
-	}
-	int* ptr = &args.version;
-	if (args.version == -1) {
-		printf("What version of VEGAS would you like to spoof to?: ");
-		fflush(stdout);
-		scanf("%d", ptr);
-	}
-	if (strcmp(args.type, " ") == 0) {
-		printf("Would you like it to be VEGAS Pro or Movie Studio? [veg/vf]: ");
-		fflush(stdout);
-		scanf("%3s", args.type);
+
+	/* this progressively */
+	while (optind < argc) {
+		const char* input = argv[optind++];
+		FILE* output_file = NULL;
+
+		{
+			uint8_t file_version = 0;
+			enum types file_type = TYPES_UNKNOWN;
+
+			printf("Input file name: %s\n", input);
+			/* print information about the input file */
+			FILE* input_file = fopen(input, "rb");
+			if (!input_file) {
+				fprintf(stderr, "[ERROR]: Error opening input file %s!\n\n", input);
+				continue;
+			}
+
+			if (fgetc(input_file) == EOF) {
+				fprintf(stderr, "[ERROR]: Input file \"%s\" is empty.\n", input);
+				fclose(input_file);
+				continue;
+			}
+
+			if (get_file_information(input_file, &file_version, &file_type)) {
+				fprintf(stderr, "[ERROR]: Failed to get file information for input file \"%s\"!\n", input);
+				fclose(input_file);
+				continue;
+			}
+
+			printf("Input file version: %u\n", file_version);
+			printf("Input file type: %s\n\n", type_to_string(file_type));
+
+			{
+				/* open the output file... */
+				char* basec = msvpvf_internal_strdup(input);
+				char* bname = basename(basec);
+				int ext = strrchr(bname, '.') - bname;
+
+				/* create the output filename */
+				int needed_size = snprintf(NULL, 0, "V%u_%.*s.%s", version, ext, bname, type_to_extension(type));
+				char* output = malloc((needed_size + 1) * sizeof(char));
+				if (!output) {
+					fprintf(stderr, "[ERROR]: Failed to allocate memory for output string!\n");
+					free(basec);
+					return 1;
+				}
+
+				snprintf(output, needed_size + 1, "V%u_%.*s.%s", version, ext, bname, type_to_extension(type));
+				printf("Output filename: %s\n", output);
+
+				output_file = fopen(output, "w+b");
+				if (!output_file) {
+					fprintf(stderr, "[ERROR]: Failed to open output file %s! Do you have write permissions?\n", output);
+					free(basec);
+					free(output);
+					continue;
+				}
+				free(basec);
+				free(output);
+			}
+
+			copy_file(input_file, output_file);
+
+			fflush(stdout);
+			fclose(input_file);
+		}
+
+		set_file_information(output_file, version, type);
+		fclose(output_file);
 	}
-	fflush(stdout);
-	if (strcmp(args.output, " ") == 0) { /* string manipulation hell */
-		char* temp = (char*)calloc(256, sizeof(char));
-		temp[0] = '\0';
-		char str_version[4];
-		sprintf(str_version, "V%d", args.version);
-		strncat(temp, str_version, 4);
-		strncat(temp, "_", 2);
-		strncat(temp, basename(args.input), 248);
-		strcpy(temp, strremove(temp, strrchr(basename(args.input), ('.')))); /* remove file extension */
-		strncat(temp, ".", 2);
-		strncat(temp, args.type, 4);
-		strncpy(args.output, temp, 255);
-		free(temp);
-	}
-	if (strcmp(args.type, "veg") == 0) {
-		const unsigned char T[] = {0xEF, 0x29, 0xC4, 0x46, 0x4A, 0x90, 0xD2, 0x11, 0x87, 0x22, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A};
-		for (option_index = 0; option_index <= 15; option_index++) { /* this line is repeated, but it's probably best to just leave it be. */
-			magic[option_index] = T[option_index];
-		}
-	} else if (strcmp(args.type, "vf") == 0) {
-		const unsigned char T[] = {0xF6, 0x1B, 0x3C, 0x53, 0x35, 0xD6, 0xF3, 0x43, 0x8A, 0x90, 0x64, 0xB8, 0x87, 0x23, 0x1F, 0x7F};
-		for (option_index = 0; option_index <= 15; option_index++) {
-			magic[option_index] = T[option_index];
-		}
-	} else {
-		fprintf(stderr, "Type %s is invalid!", args.type);
-		return 1;
-	}
-	copy_file(args.input, args.output);
-#ifdef _WIN32 /* disallowed characters in filenames */
-	if (strcspn(args.input, "<>:\"/\\|?*") == strlen(args.input)+1) {
-#elif defined(__unix__)
-	if (strcspn(args.input, "/") == strlen(args.input)+1) {
-#else
-	if (NULL) {
-#endif
-		fprintf(stderr, "Invalid output filename detected! Exiting...");
-		return 1;
-	}
-	outfile = fopen(args.output, "r+b");
-	if (outfile == NULL) {
-		fprintf(stderr, "Failed to open file %s! Do you have write permissions?", args.output);
-		return 1;
-	}
-	set_data(&magic, args.version, outfile);
-	fclose(outfile);
+
 	return 0;
 }