diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d1c6fb05..8c1d1caab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1213,6 +1213,7 @@ IF(FTE_PLUG_HL2) plugins/plugin.c plugins/hl2/hl2.c plugins/hl2/fs_vpk.c + plugins/hl2/img_vtf.c ) SET_TARGET_PROPERTIES(plug_hl2 PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;MULTITHREAD;${FTE_LIB_DEFINES}") TARGET_LINK_LIBRARIES(plug_hl2 ${SYS_LIBS} ${ZLIB_LIBRARIES}) diff --git a/plugins/Makefile b/plugins/Makefile index b1d886996..b44d6b42e 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -415,7 +415,7 @@ $(PLUG_PREFIX)openssl$(PLUG_NATIVE_EXT): net_ssl_openssl.c plugin.c ##################### #for compat with half-life 2's file formats -$(PLUG_PREFIX)hl2$(PLUG_NATIVE_EXT): hl2/fs_vpk.c hl2/hl2.c plugin.c +$(PLUG_PREFIX)hl2$(PLUG_NATIVE_EXT): hl2/fs_vpk.c hl2/img_vtf.c hl2/hl2.c plugin.c $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -DMULTITHREAD -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) #NATIVE_PLUGINS+=hl2 diff --git a/plugins/hl2/fs_vpk.c b/plugins/hl2/fs_vpk.c index ef31aea17..a7d753db0 100644 --- a/plugins/hl2/fs_vpk.c +++ b/plugins/hl2/fs_vpk.c @@ -8,9 +8,6 @@ static plugthreadfuncs_t *threading; #define Sys_LockMutex(m) (threading?threading->LockMutex(m):true) #define Sys_UnlockMutex if(threading)threading->UnlockMutex #define Sys_DestroyMutex if(threading)threading->DestroyMutex -#define Z_Malloc(x) calloc(x,1) -#define BZ_Malloc malloc -#define Z_Free free @@ -104,9 +101,9 @@ static void QDECL FSVPK_ClosePath(searchpathfuncs_t *handle) pak->fragments[i]->pub.ClosePath(&pak->fragments[i]->pub); pak->fragments[i] = NULL; } - Z_Free(pak->fragments); - Z_Free(pak->treedata); - Z_Free(pak); + plugfuncs->Free(pak->fragments); + plugfuncs->Free(pak->treedata); + plugfuncs->Free(pak); } static void QDECL FSVPK_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)) { @@ -287,7 +284,7 @@ static qboolean QDECL VFSVPK_Close(vfsfile_t *vfs) { vfsvpk_t *vfsp = (void*)vfs; FSVPK_ClosePath(&vfsp->parentpak->pub); //tell the parent that we don't need it open any more (reference counts) - Z_Free(vfsp); //free ourselves. + plugfuncs->Free(vfsp); //free ourselves. return true; } static vfsfile_t *QDECL FSVPK_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode) @@ -305,12 +302,12 @@ static vfsfile_t *QDECL FSVPK_OpenVFS(searchpathfuncs_t *handle, flocation_t *lo return NULL; pack = pack->fragments[frag]; - vfs = Z_Malloc(sizeof(vfsvpk_t)); + vfs = plugfuncs->Malloc(sizeof(vfsvpk_t)); vfs->parentpak = pack; if (!Sys_LockMutex(pack->mutex)) { - Z_Free(vfs); + plugfuncs->Free(vfs); return NULL; } vfs->parentpak->references++; @@ -470,12 +467,12 @@ static searchpathfuncs_t *QDECL FSVPK_LoadArchive (vfsfile_t *file, searchpathfu return NULL; } - tree = BZ_Malloc(header.tablesize); + tree = plugfuncs->Malloc(header.tablesize); read = VFS_READ(packhandle, tree, header.tablesize); numpackfiles = FSVPK_WalkTree(NULL, tree, tree+read); - vpk = (vpk_t*)Z_Malloc (sizeof (*vpk) + sizeof(*vpk->files)*(numpackfiles-1)); + vpk = (vpk_t*)plugfuncs->Malloc (sizeof (*vpk) + sizeof(*vpk->files)*(numpackfiles-1)); vpk->treedata = tree; vpk->treesize = read; vpk->numfiles = numpackfiles; @@ -503,7 +500,7 @@ static searchpathfuncs_t *QDECL FSVPK_LoadArchive (vfsfile_t *file, searchpathfu vpk->pub.GeneratePureCRC = FSVPK_GeneratePureCRC; vpk->pub.OpenVFS = FSVPK_OpenVFS; - vpk->fragments = Z_Malloc(vpk->numfragments*sizeof(*vpk->fragments)); + vpk->fragments = plugfuncs->Malloc(vpk->numfragments*sizeof(*vpk->fragments)); for(frag = 0; frag < vpk->numfragments; frag++) { flocation_t loc; @@ -521,7 +518,7 @@ static searchpathfuncs_t *QDECL FSVPK_LoadArchive (vfsfile_t *file, searchpathfu if (!packhandle) continue; - vpk->fragments[frag] = f = (vpk_t*)Z_Malloc(sizeof(*f)); + vpk->fragments[frag] = f = (vpk_t*)plugfuncs->Malloc(sizeof(*f)); // Q_strncpyz(f->descname, splitname, sizeof(f->descname)); f->handle = packhandle; // f->rawsize = VFS_GETLEN(f->raw); diff --git a/plugins/hl2/hl2.c b/plugins/hl2/hl2.c index 4db097a56..9624e2b98 100644 --- a/plugins/hl2/hl2.c +++ b/plugins/hl2/hl2.c @@ -1,8 +1,13 @@ #include "../plugin.h" qboolean VPK_Init(void); +qboolean VTF_Init(void); qboolean Plug_Init(void) { - return VPK_Init(); + if (!VPK_Init()) + return false; + if (!VTF_Init()) + return false; + return true; } diff --git a/plugins/hl2/img_vtf.c b/plugins/hl2/img_vtf.c new file mode 100644 index 000000000..c09963c13 --- /dev/null +++ b/plugins/hl2/img_vtf.c @@ -0,0 +1,238 @@ +#include "../plugin.h" + +static plugimagefuncs_t *imagefuncs; + +//many of these look like dupes, not really sure how they're meant to work. probably legacy. +typedef enum { + VMF_INVALID=-1, + VMF_RGBA8=0, +// VMF_ABGR8=1, + VMF_RGB8=2, + VMF_BGR8=3, +// VMF_RGB565=4, + VMF_I8=5, + VMF_IA8=6, +// VMF_P8=7, +// VMF_A8=8, +// VMF_RGB8_BS=9, +// VMF_BGR8_BS=10, +// VMF_ARGB_BS=11, + VMF_BGRA8=12, + VMF_BC1=13, + VMF_BC2=14, + VMF_BC3=15, + VMF_BGRX8=16, +// VMF_BGR565=17, +// VMF_BGRX5551=18, +// VMF_BGRA4444=19, + VMF_BC1A=20, +// VMF_BGRA5551=21, + VMF_UV88=22, +// VMF_UVWQ8=23, + VMF_RGBA16F=24, +// VMF_RGBA16N=25, +// VMF_UVLX8=26, + VMF_MAX +} fmtfmt_t; +static uploadfmt_t ImageVTF_VtfToFTE(fmtfmt_t f) +{ + switch(f) + { + case VMF_BC1: + return PTI_BC1_RGB; + case VMF_BC1A: + return PTI_BC1_RGBA; + case VMF_BC2: + return PTI_BC2_RGBA; + case VMF_BC3: + return PTI_BC3_RGBA; + case VMF_RGB8: + return PTI_RGB8; + case VMF_RGBA8: + return PTI_RGBA8; + case VMF_BGR8: + return PTI_BGR8; + case VMF_BGRA8: + return PTI_BGRA8; + case VMF_BGRX8: + return PTI_BGRX8; + case VMF_RGBA16F: + return PTI_RGBA16F; + case VMF_UV88: + return PTI_RG8; + case VMF_I8: + return PTI_L8; + case VMF_IA8: + return PTI_L8A8; + case VMF_INVALID: + return PTI_INVALID; + + default: + return PTI_INVALID; + } +} +static struct pendingtextureinfo *Image_ReadVTFFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize) +{ + //FIXME: cba with endian. + struct vtf_s + { + char magic[4]; + unsigned int major,minor; + unsigned int headersize; + + unsigned short width, height; + unsigned int flags; + unsigned short numframes, firstframe; + unsigned int pad1; + + vec3_t reflectivity; + float pad2; + + float bumpmapscale; + unsigned int imgformat; + unsigned char mipmapcount; + unsigned char lowresfmt_misaligned[4]; + unsigned char lowreswidth; + unsigned char lowresheight; + + //7.2 + unsigned char depth_misaligned[2]; + //7.3 + unsigned char pad3[3]; + unsigned int numresources; + } *vtf; + fmtfmt_t vmffmt, lrfmt; + unsigned int bw, bh, bd, bb; + qbyte *end = filedata + filesize; + unsigned int faces, frame, frames, miplevel, miplevels, img; + unsigned int w, h, d = 1; + size_t datasize; + unsigned int version; + + struct pendingtextureinfo *mips; + + vtf = (void*)filedata; + + if (memcmp(vtf->magic, "VTF\0", 4)) + return NULL; + + version = (vtf->major<<16)|vtf->minor; + if (version > 0x00070005) + { + Con_Printf("%s: VTF version %i.%i is not supported\n", fname, vtf->major, vtf->minor); + return NULL; + } + + lrfmt = (vtf->lowresfmt_misaligned[0]<<0)|(vtf->lowresfmt_misaligned[1]<<16)|(vtf->lowresfmt_misaligned[2]<<16)|(vtf->lowresfmt_misaligned[3]<<24); + vmffmt = vtf->imgformat; + + mips = NULL; + if (version >= 0x00070003) + { + int i; + struct + { + unsigned int rtype; + unsigned int rdata; //usually an offset. + } *restable = (void*)(filedata+sizeof(*vtf)); + for (i = 0; i < vtf->numresources; i++, restable++) + { + if ((restable->rtype & 0x00ffffff) == 0x30) + { + mips = plugfuncs->Malloc(sizeof(*mips)); + mips->extrafree = filedata; + filedata += restable->rdata; + break; + } + //other unknown resource types. + } + } + if (!mips) + { + mips = plugfuncs->Malloc(sizeof(*mips)); + mips->extrafree = filedata; + + //skip the header + filedata += vtf->headersize; + //and skip the low-res image too. + if (vtf->lowreswidth && vtf->lowresheight) + imagefuncs->BlockSizeForEncoding(ImageVTF_VtfToFTE(lrfmt), &bb, &bw, &bh, &bd); + else + bb=bw=bh=bd=1; + datasize = ((vtf->lowreswidth+bw-1)/bw) * ((vtf->lowresheight+bh-1)/bh) * ((1/*vtf->lowresdepth*/+bd-1)/bd) * bb; + filedata += datasize; + } + + //now handle the high-res image + if (mips) + { + mips->type = (vtf->flags & 0x4000)?PTI_CUBE:PTI_2D; + + mips->encoding = ImageVTF_VtfToFTE(vmffmt); + imagefuncs->BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd); + + miplevels = vtf->mipmapcount; + frames = 1;//vtf->numframes; + faces = ((mips->type==PTI_CUBE)?6:1); //no cubemaps yet. + + mips->mipcount = miplevels * frames; + while (mips->mipcount > countof(mips->mip)) + { + if (miplevels > 1) + miplevels--; + else + frames--; + mips->mipcount = miplevels * frames; + } + if (!mips->mipcount) + { + plugfuncs->Free(mips); + return NULL; + } + for (miplevel = vtf->mipmapcount; miplevel-- > 0;) + { //smallest to largest, which is awkward. + w = vtf->width>>miplevel; + h = vtf->height>>miplevel; + if (!w) + w = 1; + if (!h) + h = 1; + datasize = ((w+bw-1)/bw) * ((h+bh-1)/bh) * ((d+bd-1)/bd) * bb; + for (frame = 0; frame < vtf->numframes; frame++) + { + if (miplevel < miplevels) + { + img = miplevel + frame*miplevels; + if (img >= countof(mips->mip)) + break; //erk? + if (filedata + datasize > end) + break; //no more data here... + mips->mip[img].width = w; + mips->mip[img].height = h; + mips->mip[img].depth = faces; + mips->mip[img].data = filedata; + mips->mip[img].datasize = datasize*faces; + } + filedata += datasize*faces; + } + } + } + return mips; +} + +static plugimageloaderfuncs_t vtffuncs = +{ + "Valve Texture File", + sizeof(struct pendingtextureinfo), + true, + Image_ReadVTFFile, +}; + +qboolean VTF_Init(void) +{ + imagefuncs = plugfuncs->GetEngineInterface(plugimagefuncs_name, sizeof(*imagefuncs)); + if (!imagefuncs) + return false; + return plugfuncs->ExportInterface(plugimageloaderfuncs_name, &vtffuncs, sizeof(vtffuncs)); +} +