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