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: These must be defined in the implementation file or all.h
48// LOG_INFO, LOG_ERROR, LOG_WARN, log_message
49// If you don't have them, you can define fallbacks here.
50
51static VfsEntry* g_entries = NULL;
52static uint32_t g_num_entries = 0;
53static char g_pak_path[512] = {0};
54static int g_disk_mode = 0;
55
56void vfs_init(const char* pak_path) {
57 const char* debug_env = getenv("DEBUG");
58 if (debug_env && (strcmp(debug_env, "1") == 0 || strcmp(debug_env, "true") == 0)) {
59 g_disk_mode = 1;
60 TraceLog(LOG_INFO, "VFS: Operating in Disk Mode (DEBUG enabled)");
61 return;
62 }
63
64 FILE* f = fopen(pak_path, "rb");
65 if (!f) {
66 TraceLog(LOG_ERROR, "VFS: Failed to open pak file: %s (Error: %s)", pak_path, strerror(errno));
67 TraceLog(LOG_WARNING, "VFS: Falling back to Disk Mode");
68 g_disk_mode = 1;
69 return;
70 }
71
72 VfsHeader header;
73 if (fread(&header, sizeof(VfsHeader), 1, f) != 1) {
74 TraceLog(LOG_ERROR, "VFS: Failed to read header from %s", pak_path);
75 fclose(f);
76 g_disk_mode = 1;
77 return;
78 }
79
80 if (memcmp(header.magic, "DRP1", 4) != 0) {
81 TraceLog(LOG_ERROR, "VFS: Invalid magic in %s", pak_path);
82 fclose(f);
83 g_disk_mode = 1;
84 return;
85 }
86
87 g_num_entries = header.num_files;
88 g_entries = (VfsEntry*)malloc(sizeof(VfsEntry) * g_num_entries);
89 if (fread(g_entries, sizeof(VfsEntry), g_num_entries, f) != g_num_entries) {
90 TraceLog(LOG_ERROR, "VFS: Failed to read index table from %s", pak_path);
91 free(g_entries);
92 g_entries = NULL;
93 fclose(f);
94 g_disk_mode = 1;
95 return;
96 }
97
98 fclose(f);
99 strncpy(g_pak_path, pak_path, sizeof(g_pak_path) - 1);
100 TraceLog(LOG_INFO, "VFS: Loaded %u files from %s", g_num_entries, pak_path);
101}
102
103void vfs_shutdown(void) {
104 if (g_entries) {
105 free(g_entries);
106 g_entries = NULL;
107 }
108}
109
110VfsFile* vfs_open(const char* path) {
111 if (g_disk_mode) {
112 FILE* f = fopen(path, "rb");
113 if (!f) return NULL;
114 VfsFile* vf = (VfsFile*)malloc(sizeof(VfsFile));
115 vf->f = f;
116 fseek(f, 0, SEEK_END);
117 vf->size = ftell(f);
118 fseek(f, 0, SEEK_SET);
119 vf->start = 0;
120 vf->pos = 0;
121 vf->is_pak = 0;
122 return vf;
123 }
124
125 for (uint32_t i = 0; i < g_num_entries; i++) {
126 if (strcmp(g_entries[i].path, path) == 0) {
127 FILE* f = fopen(g_pak_path, "rb");
128 if (!f) return NULL;
129 VfsFile* vf = (VfsFile*)malloc(sizeof(VfsFile));
130 vf->f = f;
131 vf->start = g_entries[i].offset;
132 vf->size = g_entries[i].size;
133 vf->pos = 0;
134 vf->is_pak = 1;
135 fseek(f, (long)vf->start, SEEK_SET);
136 return vf;
137 }
138 }
139
140 TraceLog(LOG_WARNING, "VFS: File not found: %s", path);
141 return NULL;
142}
143
144size_t vfs_fread(void* ptr, size_t size, size_t nmemb, VfsFile* file) {
145 size_t total_to_read = size * nmemb;
146 if (file->pos + total_to_read > file->size) {
147 total_to_read = (size_t)(file->size - file->pos);
148 }
149
150 if (total_to_read <= 0) return 0;
151
152 size_t read_bytes = fread(ptr, 1, total_to_read, file->f);
153 file->pos += read_bytes;
154 return read_bytes / size;
155}
156
157int vfs_fseek(VfsFile* file, int64_t offset, int whence) {
158 int64_t new_pos;
159 switch (whence) {
160 case SEEK_SET: new_pos = offset; break;
161 case SEEK_CUR: new_pos = (int64_t)file->pos + offset; break;
162 case SEEK_END: new_pos = (int64_t)file->size + offset; break;
163 default: return -1;
164 }
165
166 if (new_pos < 0) new_pos = 0;
167 if (new_pos > (int64_t)file->size) new_pos = (int64_t)file->size;
168
169 file->pos = (uint64_t)new_pos;
170 return fseek(file->f, (long)(file->start + file->pos), SEEK_SET);
171}
172
173int64_t vfs_ftell(VfsFile* file) {
174 return (int64_t)file->pos;
175}
176
177void vfs_fclose(VfsFile* file) {
178 if (file->f) fclose(file->f);
179 free(file);
180}
181
182void* vfs_read(const char* path, size_t* out_size) {
183 VfsFile* f = vfs_open(path);
184 if (!f) return NULL;
185
186 void* data = malloc((size_t)f->size + 1);
187 if (!data) {
188 vfs_fclose(f);
189 return NULL;
190 }
191
192 if (vfs_fread(data, 1, (size_t)f->size, f) != f->size) {
193 free(data);
194 vfs_fclose(f);
195 return NULL;
196 }
197
198 ((char*)data)[f->size] = '\0';
199
200 if (out_size) *out_size = (size_t)f->size;
201 vfs_fclose(f);
202 return data;
203}
204
205void vfs_free(void* data) {
206 free(data);
207}
208
209#endif // VFS_IMPLEMENTATION
210
211#endif // VFS_H