libraries/vfs.h raw
  1#ifndef VFS_H
  2#define VFS_H
  3
  4#include <stddef.h>
  5#include <stdint.h>
  6#include <stdio.h>
  7#include <stdlib.h>
  8#include <string.h>
  9
 10typedef struct {
 11	char path[256];
 12	uint64_t offset;
 13	uint64_t size;
 14} VfsEntry;
 15
 16typedef struct {
 17	char magic[4];
 18	uint32_t num_files;
 19} VfsHeader;
 20
 21typedef struct {
 22	FILE* f;
 23	uint64_t start;
 24	uint64_t size;
 25	uint64_t pos;
 26	int is_pak;
 27} VfsFile;
 28
 29void vfs_init(const char* pak_path);
 30void vfs_shutdown(void);
 31
 32// Basic API
 33void* vfs_read(const char* path, size_t* out_size);
 34void vfs_free(void* data);
 35
 36// Extended API for VFS-like behavior
 37VfsFile* vfs_open(const char* path);
 38size_t vfs_fread(void* ptr, size_t size, size_t nmemb, VfsFile* file);
 39int vfs_fseek(VfsFile* file, int64_t offset, int whence);
 40int64_t vfs_ftell(VfsFile* file);
 41void vfs_fclose(VfsFile* file);
 42
 43#ifdef VFS_IMPLEMENTATION
 44
 45#include <errno.h>
 46
 47// Note: Standard printf is used for logging.
 48
 49static VfsEntry* g_entries = NULL;
 50static uint32_t g_num_entries = 0;
 51static char g_pak_path[512] = {0};
 52static int g_disk_mode = 0;
 53
 54void vfs_init(const char* pak_path) {
 55	const char* debug_env = getenv("DEBUG");
 56	if (debug_env && (strcmp(debug_env, "1") == 0 || strcmp(debug_env, "true") == 0)) {
 57		g_disk_mode = 1;
 58		printf("VFS INFO: Operating in Disk Mode (DEBUG enabled)\n");
 59		fflush(stdout);
 60		return;
 61	}
 62
 63	FILE* f = fopen(pak_path, "rb");
 64	if (!f) {
 65		printf("VFS ERROR: Failed to open pak file: %s (Error: %s)\n", pak_path, strerror(errno));
 66		printf("VFS WARNING: Falling back to Disk Mode\n");
 67		fflush(stdout);
 68		g_disk_mode = 1;
 69		return;
 70	}
 71
 72	VfsHeader header;
 73	if (fread(&header, sizeof(VfsHeader), 1, f) != 1) {
 74		printf("VFS ERROR: Failed to read header from %s\n", pak_path);
 75		fflush(stdout);
 76		fclose(f);
 77		g_disk_mode = 1;
 78		return;
 79	}
 80
 81	if (memcmp(header.magic, "DRP1", 4) != 0) {
 82		printf("VFS ERROR: Invalid magic in %s\n", pak_path);
 83		fflush(stdout);
 84		fclose(f);
 85		g_disk_mode = 1;
 86		return;
 87	}
 88
 89	g_num_entries = header.num_files;
 90	g_entries = (VfsEntry*)malloc(sizeof(VfsEntry) * g_num_entries);
 91	if (fread(g_entries, sizeof(VfsEntry), g_num_entries, f) != g_num_entries) {
 92		printf("VFS ERROR: Failed to read index table from %s\n", pak_path);
 93		fflush(stdout);
 94		free(g_entries);
 95		g_entries = NULL;
 96		fclose(f);
 97		g_disk_mode = 1;
 98		return;
 99	}
100
101	fclose(f);
102	strncpy(g_pak_path, pak_path, sizeof(g_pak_path) - 1);
103	printf("VFS INFO: Loaded %u files from %s\n", g_num_entries, pak_path);
104	fflush(stdout);
105}
106
107void vfs_shutdown(void) {
108	if (g_entries) {
109		free(g_entries);
110		g_entries = NULL;
111	}
112}
113
114VfsFile* vfs_open(const char* path) {
115	if (g_disk_mode) {
116		FILE* f = fopen(path, "rb");
117		if (!f) return NULL;
118		VfsFile* vf = (VfsFile*)malloc(sizeof(VfsFile));
119		vf->f = f;
120		fseek(f, 0, SEEK_END);
121		vf->size = ftell(f);
122		fseek(f, 0, SEEK_SET);
123		vf->start = 0;
124		vf->pos = 0;
125		vf->is_pak = 0;
126		return vf;
127	}
128
129	for (uint32_t i = 0; i < g_num_entries; i++) {
130		if (strcmp(g_entries[i].path, path) == 0) {
131			FILE* f = fopen(g_pak_path, "rb");
132			if (!f) return NULL;
133			VfsFile* vf = (VfsFile*)malloc(sizeof(VfsFile));
134			vf->f = f;
135			vf->start = g_entries[i].offset;
136			vf->size = g_entries[i].size;
137			vf->pos = 0;
138			vf->is_pak = 1;
139			fseek(f, (long)vf->start, SEEK_SET);
140			return vf;
141		}
142	}
143
144	printf("VFS WARNING: File not found: %s\n", path);
145	fflush(stdout);
146	return NULL;
147}
148
149size_t vfs_fread(void* ptr, size_t size, size_t nmemb, VfsFile* file) {
150	size_t total_to_read = size * nmemb;
151	if (file->pos + total_to_read > file->size) {
152		total_to_read = (size_t)(file->size - file->pos);
153	}
154
155	if (total_to_read <= 0) return 0;
156
157	size_t read_bytes = fread(ptr, 1, total_to_read, file->f);
158	file->pos += read_bytes;
159	return read_bytes / size;
160}
161
162int vfs_fseek(VfsFile* file, int64_t offset, int whence) {
163	int64_t new_pos;
164	switch (whence) {
165		case SEEK_SET: new_pos = offset; break;
166		case SEEK_CUR: new_pos = (int64_t)file->pos + offset; break;
167		case SEEK_END: new_pos = (int64_t)file->size + offset; break;
168		default: return -1;
169	}
170
171	if (new_pos < 0) new_pos = 0;
172	if (new_pos > (int64_t)file->size) new_pos = (int64_t)file->size;
173
174	file->pos = (uint64_t)new_pos;
175	return fseek(file->f, (long)(file->start + file->pos), SEEK_SET);
176}
177
178int64_t vfs_ftell(VfsFile* file) {
179	return (int64_t)file->pos;
180}
181
182void vfs_fclose(VfsFile* file) {
183	if (file->f) fclose(file->f);
184	free(file);
185}
186
187void* vfs_read(const char* path, size_t* out_size) {
188	VfsFile* f = vfs_open(path);
189	if (!f) return NULL;
190
191	void* data = malloc((size_t)f->size + 1);
192	if (!data) {
193		vfs_fclose(f);
194		return NULL;
195	}
196
197	if (vfs_fread(data, 1, (size_t)f->size, f) != f->size) {
198		free(data);
199		vfs_fclose(f);
200		return NULL;
201	}
202
203	((char*)data)[f->size] = '\0';
204
205	if (out_size) *out_size = (size_t)f->size;
206	vfs_fclose(f);
207	return data;
208}
209
210void vfs_free(void* data) {
211	free(data);
212}
213
214#endif // VFS_IMPLEMENTATION
215
216#endif // VFS_H