Jelajahi Sumber

start implementing vpk reader

Ivan Avdeev 6 tahun lalu
induk
melakukan
95742caba8
6 mengubah file dengan 259 tambahan dan 7 penghapusan
  1. 3 4
      Makefile
  2. 1 1
      src/.ycm_extra_conf.py
  3. 16 2
      src/OpenSource.c
  4. 197 0
      src/collection.c
  5. 18 0
      src/collection.h
  6. 24 0
      src/vpk.h

+ 3 - 4
Makefile

@@ -10,13 +10,12 @@ MAP ?= d1_trainstation_01
 DEPFLAGS = -MMD -MP
 COMPILE.c = $(CC) -std=gnu99 $(CFLAGS) $(DEPFLAGS) -MT $@ -MF $@.d
 
-all: run
-
 $(OBJDIR)/%.c.o: %.c
 	@mkdir -p $(dir $@)
 	$(COMPILE.c) -c $< -o $@
 
 TOOL_EXE = $(OBJDIR)/OpenSource
+all: $(TOOL_EXE)
 TOOL_SRCS = \
 	src/atto/src/app_linux.c \
 	src/atto/src/app_x11.c \
@@ -43,9 +42,9 @@ clean:
 	rm -f $(TOOL_OBJS) $(TOOL_DEPS) $(TOOL_EXE)
 
 run: $(TOOL_EXE)
-	$(TOOL_EXE) -d mnt/hl2_misc -d mnt/hl2_pak -d mnt/hl2_textures -d ~/.local/share/Steam/steamapps/common/Half-Life\ 2/hl2 $(MAP)
+	$(TOOL_EXE) -p ~/.local/share/Steam/steamapps/common/Half-Life\ 2/hl2/hl2_textures_dir.vpk -d mnt/hl2_misc -d mnt/hl2_pak -d mnt/hl2_textures -d ~/.local/share/Steam/steamapps/common/Half-Life\ 2/hl2 $(MAP)
 
 debug: $(TOOL_EXE)
-	gdb --args $(TOOL_EXE) -d mnt/hl2_misc -d mnt/hl2_pak -d mnt/hl2_textures -d ~/.local/share/Steam/steamapps/common/Half-Life\ 2/hl2 $(MAP)
+	gdb --args $(TOOL_EXE) -p ~/.local/share/Steam/steamapps/common/Half-Life\ 2/hl2/hl2_textures_dir.vpk -d mnt/hl2_misc -d mnt/hl2_pak -d mnt/hl2_textures -d ~/.local/share/Steam/steamapps/common/Half-Life\ 2/hl2 $(MAP)
 
 .PHONY: all clean run_tool debug_tool

+ 1 - 1
src/.ycm_extra_conf.py

@@ -7,7 +7,7 @@ RPI_FLAGS = [
     '-DATTO_PLATFORM_RPI',
     '-D_POSIX_C_SOURCE=200809L',
     '-std=gnu99']
-COMMON_FLAGS = ['-Wall', '-Wextra', '-pedantic', '-std=c99', '-I.', '-I../atto']
+COMMON_FLAGS = ['-Wall', '-Wextra', '-pedantic', '-std=c99', '-I.', '-I../atto', '-Iatto', '-Isrc/atto']
 
 def FlagsForFile(filename, **kwargs):
     flags = COMMON_FLAGS

+ 16 - 2
src/OpenSource.c

@@ -431,7 +431,21 @@ void attoAppInit(struct AAppProctable *proctable) {
 
 	for (int i = 1; i < a_app_state->argc; ++i) {
 		const char *argv = a_app_state->argv[i];
-		if (strcmp(argv, "-d") == 0) {
+		if (strcmp(argv, "-p") == 0) {
+			if (i == a_app_state->argc - 1) {
+				aAppDebugPrintf("-p requires an argument");
+				goto print_usage_and_exit;
+			}
+			const char *value = a_app_state->argv[++i];
+
+			if (free_collection >= max_collections) {
+				aAppDebugPrintf("Too many collections specified: %s", value);
+				goto print_usage_and_exit;
+			}
+
+			struct VPKCollection vc;
+			vpkCollectionCreate(&vc, value, &stack_persistent, &stack_temp);
+		} else if (strcmp(argv, "-d") == 0) {
 			if (i == a_app_state->argc - 1) {
 				aAppDebugPrintf("-d requires an argument");
 				goto print_usage_and_exit;
@@ -439,7 +453,7 @@ void attoAppInit(struct AAppProctable *proctable) {
 			const char *value = a_app_state->argv[++i];
 
 			if (free_collection >= max_collections) {
-				aAppDebugPrintf("Too many fs collections specified: %s", value);
+				aAppDebugPrintf("Too many collections specified: %s", value);
 				goto print_usage_and_exit;
 			}
 

+ 197 - 0
src/collection.c

@@ -1,5 +1,7 @@
 #include "collection.h"
 #include "common.h"
+#include "vpk.h"
+#include <alloca.h>
 
 enum CollectionOpenResult collectionChainOpen(struct ICollection *collection,
 		const char *name, enum FileType type, struct IFile **out_file) {
@@ -130,3 +132,198 @@ void filesystemCollectionCreate(struct FilesystemCollection *collection, const c
 	collection->head.open = filesystemCollectionOpen;
 	collection->head.close = filesystemCollectionClose;
 }
+
+struct StringView {
+	const char *s;
+	int len;
+};
+#define PRI_SV "%.*s"
+#define PASS_SV(sv) sv.len, sv.s
+#define PASS_PSV(sv) sv->len, sv->s
+
+static struct StringView readString(const char **c, const char *end) {
+	struct StringView ret = { *c, 0 };
+	while (*c < end && **c != '\0') ++(*c);
+	ret.len = *c - ret.s;
+	++(*c);
+	return ret;
+}
+
+enum { VPKFilePartDir = 0, VPKFilePartArchive = 1, VPKFilePart_MAX };
+
+struct VPKFileMetadata {
+	struct StringView filename;
+	int archive;
+	struct {
+		size_t off, size;
+	} parts[VPKFilePart_MAX];
+};
+
+static void vpkCollectionClose(struct ICollection *collection) {
+	(void)(collection);
+	/* FIXME close handles */
+}
+
+static enum CollectionOpenResult vpkCollectionOpen(struct ICollection *collection,
+		const char *name, enum FileType type, struct IFile **out_file) {
+	struct VPKCollection *vpkc = (struct VPKCollection*)collection;
+
+	return CollectionOpen_NotFound;
+}
+
+void vpkCollectionCreate(struct VPKCollection *collection, const char *dir_filename, struct Stack *persistent, struct Stack *temp) {
+	PRINTF("Opening collection %s", dir_filename);
+
+	void *const temp_stack_top = stackGetCursor(temp);
+
+	collection->directory = aFileMapOpen(dir_filename);
+	if (!collection->directory.map) {
+		PRINTF("Cannot open %s", dir_filename);
+		exit(-1);
+	}
+
+	const char *dir = collection->directory.map;
+	const size_t size = collection->directory.size;
+
+	if (size <= sizeof(struct VPK2Header)) {
+		PRINT("VPK header is too small");
+		exit(-1);
+	}
+
+	const struct VPK2Header *header = collection->directory.map;
+
+	if (header->signature != VPK_SIGNATURE) {
+		PRINTF("Wrong VPK signature %08x", header->signature);
+		exit(-1);
+	}
+
+	if (header->version != 2) {
+		PRINTF("VPK version %d is not supported", header->version);
+		exit(-1);
+	}
+
+	struct VPKFileMetadata *files_begin = stackGetCursor(persistent), *files_end = files_begin;
+
+	int max_archives = -1;
+	const char *const end = dir + size;
+	const char *c = dir + sizeof(struct VPK2Header);
+	for (;;) {
+		// read extension
+		const struct StringView ext = readString(&c, end);
+		if (ext.len == 0)
+			break;
+
+		for (;;) {
+			// read path
+			const struct StringView path = readString(&c, end);
+			if (path.len == 0)
+				break;
+
+			for (;;) {
+				// read filename
+				const struct StringView filename = readString(&c, end);
+				if (filename.len == 0)
+					break;
+
+				if ((unsigned long)(end - c) < sizeof(struct VPKTreeEntry)) {
+					PRINT("Incomplete VPKTreeEntry struct");
+					exit(-1);
+				}
+
+				const struct VPKTreeEntry *const entry = (const struct VPKTreeEntry*)c;
+				c += sizeof(struct VPKTreeEntry);
+
+				if (entry->terminator != VPK_TERMINATOR) {
+					PRINTF("Wrong terminator: %04x", entry->terminator);
+					exit(-1);
+				}
+
+				const int filename_len = 3 + path.len + filename.len + ext.len;
+				char *filename_temp = stackAlloc(temp, filename_len);
+				if (!filename_temp) {
+					PRINT("Not enough temp memory");
+					exit(-1);
+				}
+
+				memcpy(filename_temp, path.s, path.len);
+				filename_temp[path.len] = '/';
+				memcpy(filename_temp + path.len + 1, filename.s, filename.len);
+				filename_temp[path.len + 1 + filename.len] = '.';
+				memcpy(filename_temp + path.len + 1 + filename.len + 1, ext.s, ext.len);
+				filename_temp[filename_len-1] = '\0';
+
+				PRINTF("%s crc=%08x pre=%d arc=%d(%04x) off=%d len=%d",
+					filename_temp,
+					entry->crc,
+					entry->preloadBytes, entry->archive, entry->archive,
+					entry->archiveOffset, entry->archiveLength);
+
+				struct VPKFileMetadata *file = stackAlloc(persistent, sizeof(struct VPKFileMetadata));
+				if (!file) {
+					PRINT("Not enough persistent memory");
+					exit(-1);
+				}
+				memset(file, 0, sizeof(*file));
+
+				file->filename.s = filename_temp;
+				file->filename.len = filename_len - 1;
+				if (entry->preloadBytes) {
+					file->parts[VPKFilePartDir].off = c - (char*)dir;
+					file->parts[VPKFilePartDir].size = entry->preloadBytes;
+				}
+
+				if (entry->archiveLength) {
+					file->archive = entry->archive != 0x7fff ? entry->archive : -1;
+					file->parts[VPKFilePartArchive].off = entry->archiveOffset;
+					file->parts[VPKFilePartArchive].size = entry->archiveLength;
+				}
+
+				if (file->archive > max_archives)
+					max_archives = file->archive;
+
+				files_end = file + 1;
+
+				c += entry->preloadBytes;
+			} // for filenames
+		} // for paths
+	} // for extensions
+
+	// TODO sort
+	
+	// store filenames in persistent memory
+	for (struct VPKFileMetadata *file = files_begin; file != files_end; ++file) {
+		char *string = stackAlloc(persistent, file->filename.len + 1);
+		if (!string) {
+			PRINT("Not enough persistent memory");
+			exit(-1);
+		}
+
+		memcpy(string, file->filename.s, file->filename.len + 1);
+		file->filename.s = string;
+	}
+
+	// open archives
+	if (max_archives >= MAX_VPK_ARCHIVES) {
+		PRINTF("Too many archives: %d", max_archives);
+		exit(-1);
+	}
+
+	const int dir_filename_len = strlen(dir_filename) + 1;
+	char *arcname = alloca(dir_filename_len);
+	if (!arcname || dir_filename_len < 8) {
+		PRINT("WTF");
+		exit(-1);
+	}
+	memcpy(arcname, dir_filename, dir_filename_len);
+	for (int i = 0; i < max_archives; ++i) {
+		sprintf(arcname + dir_filename_len - 8, "%03d.vpk", i);
+		if (AFile_Success != aFileOpen(collection->archives+i, arcname)) {
+			PRINTF("Cannot open archive %s", arcname);
+			exit(-1);
+		}
+	}
+
+	stackFreeUpToPosition(temp, temp_stack_top);
+	collection->head.open = vpkCollectionOpen;
+	collection->head.close = vpkCollectionClose;
+}

+ 18 - 0
src/collection.h

@@ -1,5 +1,6 @@
 #pragma once
 #include "filemap.h"
+#include "mempools.h"
 #include <stddef.h>
 
 struct IFile {
@@ -53,3 +54,20 @@ struct FilesystemCollection {
 };
 
 void filesystemCollectionCreate(struct FilesystemCollection *collection, const char *dir);
+
+#define MAX_VPK_ARCHIVES 16
+
+struct VPKFileMetadata;
+
+struct VPKCollection {
+	struct ICollection head;
+	struct AFileMap directory;
+	struct AFile archives[MAX_VPK_ARCHIVES];
+	struct VPKFileMetadata *files;
+	int files_count;
+};
+
+void vpkCollectionCreate(struct VPKCollection *collection, const char *dir_filename, struct Stack *persistent, struct Stack *temp);
+
+
+

+ 24 - 0
src/vpk.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include <stdint.h>
+
+#pragma pack(1)
+#define VPK_SIGNATURE (0x55aa1234ul)
+struct VPK2Header {
+	uint32_t signature;
+	uint32_t version;
+	uint32_t treeSize;
+	uint32_t dontCareSize[4];
+};
+
+#define VPK_TERMINATOR (0xffffu)
+struct VPKTreeEntry {
+	uint32_t crc;
+	uint16_t preloadBytes;
+	uint16_t archive;
+	uint32_t archiveOffset;
+	uint32_t archiveLength;
+	uint16_t terminator;
+};
+
+#pragma pack()