Skip to content

Commit dba6527

Browse files
authored
Add support for VTMB VPKs to hl2 plugin (#329)
1 parent b5fe075 commit dba6527

4 files changed

Lines changed: 329 additions & 2 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,7 @@ IF(FTE_PLUG_HL2)
15231523
plugins/plugin.c
15241524
plugins/hl2/hl2.c
15251525
plugins/hl2/fs_vpk.c
1526+
plugins/hl2/fs_vpk_vtmb.c
15261527
plugins/hl2/img_vtf.c
15271528
plugins/hl2/mod_hl2.c
15281529
plugins/hl2/mod_vbsp.c

plugins/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ endif
509509

510510
######################################
511511
#for compat with half-life 2's file formats
512-
$(PLUG_PREFIX)hl2$(PLUG_NATIVE_EXT): hl2/fs_vpk.c hl2/img_vtf.c hl2/mod_hl2.c hl2/mat_vmt.c hl2/mod_vbsp.c hl2/hl2.c plugin.c
512+
$(PLUG_PREFIX)hl2$(PLUG_NATIVE_EXT): hl2/fs_vpk.c hl2/fs_vpk_vtmb.c hl2/img_vtf.c hl2/mod_hl2.c hl2/mat_vmt.c hl2/mod_vbsp.c hl2/hl2.c plugin.c
513513
$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -DMULTITHREAD -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS)
514514
$(call EMBEDMETA,hl2,$@,HL2 Formats,Provides support for various formats used by Valve's Source engine.)
515515
NATIVE_PLUGINS+=hl2

plugins/hl2/fs_vpk.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ static unsigned int FSVPK_WalkTree(vpk_t *vpk, const char *start, const char *en
413413
return files;
414414
}
415415

416+
searchpathfuncs_t *QDECL FSVVPK_LoadArchive(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);
417+
416418
/*
417419
=================
418420
COM_LoadPackFile
@@ -461,7 +463,8 @@ static searchpathfuncs_t *QDECL FSVPK_LoadArchive (vfsfile_t *file, searchpathfu
461463
VFS_SEEK(packhandle, 0);
462464
tablesize = 1313113;
463465
} else {
464-
return NULL;
466+
// try to load as vtmb vpk
467+
return FSVVPK_LoadArchive(file, parent, filename, desc, prefix);
465468
}
466469
} else {
467470
i = LittleLong(header.version);
@@ -539,6 +542,8 @@ static searchpathfuncs_t *QDECL FSVPK_LoadArchive (vfsfile_t *file, searchpathfu
539542
return &vpk->pub;
540543
}
541544

545+
qboolean VVPK_Init(void);
546+
542547
qboolean VPK_Init(void)
543548
{
544549
threading = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threading));
@@ -548,6 +553,10 @@ qboolean VPK_Init(void)
548553
if (!filefuncs)
549554
return false;
550555

556+
// vtmb
557+
if (!VVPK_Init())
558+
return false;
559+
551560
//we can't cope with being closed randomly. files cannot be orphaned safely.
552561
//so ask the engine to ensure we don't get closed before everything else is.
553562
plugfuncs->ExportFunction("MayShutdown", NULL);

plugins/hl2/fs_vpk_vtmb.c

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
#include "../plugin.h"
2+
#include "../../engine/common/fs.h"
3+
4+
static plugfsfuncs_t *filefuncs = NULL;
5+
static plugthreadfuncs_t *threadfuncs = NULL;
6+
#define Sys_CreateMutex() (threadfuncs?threadfuncs->CreateMutex():NULL)
7+
#define Sys_LockMutex(m) (threadfuncs?threadfuncs->LockMutex(m):true)
8+
#define Sys_UnlockMutex if(threadfuncs)threadfuncs->UnlockMutex
9+
#define Sys_DestroyMutex if(threadfuncs)threadfuncs->DestroyMutex
10+
11+
typedef struct vvpkfile {
12+
fsbucket_t bucket;
13+
char name[MAX_QPATH];
14+
size_t ofs;
15+
size_t len;
16+
} vvpkfile_t;
17+
18+
typedef struct vvpk {
19+
searchpathfuncs_t pub;
20+
char desc[MAX_OSPATH];
21+
vfsfile_t *handle;
22+
size_t num_files;
23+
vvpkfile_t *files;
24+
void *mutex;
25+
int references;
26+
} vvpk_t;
27+
28+
static void QDECL FSVVPK_GetPathDetails(searchpathfuncs_t *handle, char *out, size_t outlen)
29+
{
30+
vvpk_t *pak = (vvpk_t *)handle;
31+
*out = 0;
32+
if (pak->references != 1)
33+
Q_snprintfz(out, outlen, "(%i)", pak->references - 1);
34+
}
35+
36+
static void QDECL FSVVPK_ClosePath(searchpathfuncs_t *handle)
37+
{
38+
qboolean stillopen;
39+
vvpk_t *pak = (vvpk_t *)handle;
40+
41+
if (!Sys_LockMutex(pak->mutex))
42+
return; //ohnoes
43+
stillopen = --pak->references > 0;
44+
Sys_UnlockMutex(pak->mutex);
45+
if (stillopen)
46+
return; //not free yet
47+
48+
VFS_CLOSE(pak->handle);
49+
50+
Sys_DestroyMutex(pak->mutex);
51+
52+
plugfuncs->Free(pak->files);
53+
plugfuncs->Free(pak);
54+
}
55+
56+
static void QDECL FSVVPK_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))
57+
{
58+
vvpk_t *pak = (vvpk_t *)handle;
59+
int i;
60+
for (i = 0; i < pak->num_files; i++)
61+
AddFileHash(depth, pak->files[i].name, &pak->files[i].bucket, &pak->files[i]);
62+
}
63+
64+
static unsigned int QDECL FSVVPK_FindFile(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)
65+
{
66+
vvpkfile_t *file = (vvpkfile_t *)hashedresult;
67+
vvpk_t *pak = (vvpk_t *)handle;
68+
int i;
69+
70+
if (file)
71+
{
72+
//is this a pointer to a file in this pak?
73+
if (file < pak->files || file > pak->files + pak->num_files)
74+
return FF_NOTFOUND;
75+
}
76+
else
77+
{
78+
for (i = 0; i < pak->num_files; i++)
79+
{
80+
if (strcasecmp(filename, pak->files[i].name) == 0)
81+
{
82+
file = &pak->files[i];
83+
break;
84+
}
85+
}
86+
}
87+
88+
if (file)
89+
{
90+
if (loc)
91+
{
92+
loc->fhandle = file;
93+
*loc->rawname = 0;
94+
loc->offset = (qofs_t)-1;
95+
loc->len = file->len;
96+
}
97+
98+
return FF_FOUND;
99+
}
100+
101+
return FF_NOTFOUND;
102+
}
103+
104+
typedef struct vfsvvpk {
105+
vfsfile_t funcs;
106+
vvpk_t *parent;
107+
qofs_t startpos;
108+
qofs_t length;
109+
qofs_t currentpos;
110+
} vfsvvpk_t;
111+
112+
static int QDECL VFSVVPK_ReadBytes(struct vfsfile_s *vfs, void *buffer, int bytestoread)
113+
{
114+
vfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;
115+
int read = 0;
116+
117+
if (bytestoread + vfsp->currentpos > vfsp->length)
118+
bytestoread = vfsp->length - vfsp->currentpos;
119+
120+
if (Sys_LockMutex(vfsp->parent->mutex))
121+
{
122+
VFS_SEEK(vfsp->parent->handle, vfsp->startpos + vfsp->currentpos);
123+
read = VFS_READ(vfsp->parent->handle, buffer, bytestoread);
124+
vfsp->currentpos += read;
125+
Sys_UnlockMutex(vfsp->parent->mutex);
126+
}
127+
128+
return read;
129+
}
130+
131+
static int QDECL VFSVVPK_WriteBytes(struct vfsfile_s *vfs, const void *buffer, int bytestoread)
132+
{
133+
plugfuncs->Error("Cannot write to vpk files\n");
134+
return 0;
135+
}
136+
137+
static qofs_t QDECL VFSVVPK_Tell(struct vfsfile_s *vfs)
138+
{
139+
vfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;
140+
return vfsp->currentpos;
141+
}
142+
143+
static qofs_t QDECL VFSVVPK_GetLen(struct vfsfile_s *vfs)
144+
{
145+
vfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;
146+
return vfsp->length;
147+
}
148+
149+
static qboolean QDECL VFSVVPK_Close(vfsfile_t *vfs)
150+
{
151+
vfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;
152+
FSVVPK_ClosePath(&vfsp->parent->pub); //tell the parent that we don't need it open any more (reference counts)
153+
plugfuncs->Free(vfsp); //free ourselves.
154+
return true;
155+
}
156+
157+
static qboolean QDECL VFSVVPK_Seek(struct vfsfile_s *vfs, qofs_t pos)
158+
{
159+
vfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;
160+
if (pos > vfsp->length)
161+
return false;
162+
vfsp->currentpos = pos;
163+
return true;
164+
}
165+
166+
static vfsfile_t *QDECL FSVVPK_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)
167+
{
168+
vvpk_t *pak = (vvpk_t *)handle;
169+
vfsvvpk_t *vfs;
170+
vvpkfile_t *f = (vvpkfile_t *)loc->fhandle;
171+
172+
if (strcmp(mode, "rb") != 0)
173+
return NULL;
174+
175+
vfs = plugfuncs->Malloc(sizeof(vfsvvpk_t));
176+
177+
vfs->parent = pak;
178+
if (!Sys_LockMutex(vfs->parent->mutex))
179+
{
180+
plugfuncs->Free(vfs);
181+
return NULL;
182+
}
183+
184+
vfs->parent->references++;
185+
Sys_UnlockMutex(vfs->parent->mutex);
186+
187+
vfs->startpos = f->ofs;
188+
vfs->length = loc->len;
189+
vfs->currentpos = 0;
190+
191+
#ifdef _DEBUG
192+
Q_strlcpy(vfs->funcs.dbgname, f->name, sizeof(vfs->funcs.dbgname));
193+
#endif
194+
vfs->funcs.Close = VFSVVPK_Close;
195+
vfs->funcs.GetLen = VFSVVPK_GetLen;
196+
vfs->funcs.ReadBytes = VFSVVPK_ReadBytes;
197+
vfs->funcs.Seek = VFSVVPK_Seek;
198+
vfs->funcs.Tell = VFSVVPK_Tell;
199+
vfs->funcs.WriteBytes = VFSVVPK_WriteBytes; //not supported
200+
201+
return (vfsfile_t *)vfs;
202+
}
203+
204+
static void QDECL FSVVPK_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)
205+
{
206+
vfsfile_t *f;
207+
f = FSVVPK_OpenVFS(handle, loc, "rb");
208+
if (!f)
209+
return;
210+
VFS_READ(f, buffer, loc->len);
211+
VFS_CLOSE(f);
212+
}
213+
214+
static int QDECL FSVVPK_EnumerateFiles(searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)
215+
{
216+
vvpk_t *pak = (vvpk_t *)handle;
217+
int i;
218+
219+
for (i = 0; i < pak->num_files; i++)
220+
{
221+
if (filefuncs->WildCmp(match, pak->files[i].name))
222+
{
223+
if (!func(pak->files[i].name, pak->files[i].len, 0, parm, handle))
224+
return false;
225+
}
226+
}
227+
228+
return true;
229+
}
230+
231+
static int QDECL FSVVPK_GeneratePureCRC(searchpathfuncs_t *handle, const int *seed)
232+
{
233+
vvpk_t *pak = (vvpk_t *)handle;
234+
return filefuncs->BlockChecksum(pak->files, sizeof(*pak->files) * pak->num_files);
235+
}
236+
237+
searchpathfuncs_t *QDECL FSVVPK_LoadArchive(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)
238+
{
239+
vvpk_t *pak;
240+
int i;
241+
int fsize;
242+
uint32_t num_files, ofs_files;
243+
uint8_t sentinel;
244+
245+
// sanity checks
246+
if (!file) return NULL;
247+
if (prefix && *prefix) return NULL;
248+
249+
// validate footer
250+
fsize = VFS_GETLEN(file);
251+
if (fsize < 9)
252+
return NULL;
253+
VFS_SEEK(file, fsize - 9);
254+
VFS_READ(file, &num_files, 4);
255+
VFS_READ(file, &ofs_files, 4);
256+
VFS_READ(file, &sentinel, 1);
257+
if (sentinel != 0)
258+
return NULL;
259+
260+
// alloc
261+
pak = plugfuncs->Malloc(sizeof(*pak));
262+
pak->files = plugfuncs->Malloc(sizeof(*pak->files) * num_files);
263+
264+
// read tree
265+
VFS_SEEK(file, ofs_files);
266+
for (i = 0; i < num_files; i++)
267+
{
268+
uint32_t len_name;
269+
uint32_t ofs_data;
270+
uint32_t len_data;
271+
VFS_READ(file, &len_name, 4);
272+
if (len_name > MAX_QPATH - 1)
273+
{
274+
Con_Printf("ERROR: filename in vpk is too long\n");
275+
return NULL;
276+
}
277+
278+
VFS_READ(file, pak->files[i].name, len_name);
279+
VFS_READ(file, &ofs_data, 4);
280+
VFS_READ(file, &len_data, 4);
281+
282+
pak->files[i].ofs = ofs_data;
283+
pak->files[i].len = len_data;
284+
}
285+
286+
// setup info
287+
pak->references++;
288+
pak->num_files = num_files;
289+
pak->handle = file;
290+
strcpy(pak->desc, desc);
291+
VFS_SEEK(pak->handle, 0);
292+
pak->mutex = Sys_CreateMutex();
293+
294+
// setup funcs
295+
pak->pub.fsver = FSVER;
296+
pak->pub.GetPathDetails = FSVVPK_GetPathDetails;
297+
pak->pub.ClosePath = FSVVPK_ClosePath;
298+
pak->pub.BuildHash = FSVVPK_BuildHash;
299+
pak->pub.FindFile = FSVVPK_FindFile;
300+
pak->pub.ReadFile = FSVVPK_ReadFile;
301+
pak->pub.EnumerateFiles = FSVVPK_EnumerateFiles;
302+
pak->pub.GeneratePureCRC = FSVVPK_GeneratePureCRC;
303+
pak->pub.OpenVFS = FSVVPK_OpenVFS;
304+
305+
return (searchpathfuncs_t *)pak;
306+
}
307+
308+
qboolean VVPK_Init(void)
309+
{
310+
// get funcs
311+
threadfuncs = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threadfuncs));
312+
filefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));
313+
if (!filefuncs) // threadfuncs optional
314+
return false;
315+
316+
return true;
317+
}

0 commit comments

Comments
 (0)