Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for loading JKA assets. #159

Merged
merged 10 commits into from May 1, 2023
17 changes: 17 additions & 0 deletions CVARS.rst
Expand Up @@ -45,6 +45,23 @@ New and Modified Cvars
| ``fs_game cvar``
| ``fs_forcegame cvar``

..

:Name: fs_assetsPathJKA
:Values: Foldername
:Default: "" (Not set on portable); autodetected for non-portable
:Description:
Sets the path to load JKA assets from.

..

:Name: fs_noJKA
aufau marked this conversation as resolved.
Show resolved Hide resolved
:Values: "0", "1"
:Default: "0"
:Description:
Disables loading of JKA assets even when fs_assetsPathJKA point to a valid
JKA folder.

-----------
Client-Side
-----------
Expand Down
1 change: 1 addition & 0 deletions src/client/cl_main.cpp
Expand Up @@ -2701,6 +2701,7 @@ void CL_InitRef( void ) {
ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory;
ri.CM_DrawDebugSurface = CM_DrawDebugSurface;
ri.FS_ReadFile = FS_ReadFile;
ri.FS_ReadFileSkipJKA = FS_ReadFileSkipJKA;
ri.FS_FreeFile = FS_FreeFile;
ri.FS_WriteFile = FS_WriteFile;
ri.FS_FreeFileList = FS_FreeFileList;
Expand Down
81 changes: 69 additions & 12 deletions src/qcommon/files.cpp
Expand Up @@ -216,6 +216,7 @@ typedef struct {
fileInPack_t* *hashTable; // hash table
fileInPack_t* buildBuffer; // buffer with the filenames etc.
int gvc; // game-version compatibility
qboolean isJKA; // jka assets
aufau marked this conversation as resolved.
Show resolved Hide resolved
} pack_t;

typedef struct {
Expand All @@ -235,6 +236,8 @@ static cvar_t *fs_debug;
static cvar_t *fs_homepath;
static cvar_t *fs_basepath;
static cvar_t *fs_assetspath;
static cvar_t *fs_assetspathJKA;
static cvar_t *fs_noJKA;
static cvar_t *fs_basegame;
static cvar_t *fs_copyfiles;
static cvar_t *fs_gamedirvar;
Expand Down Expand Up @@ -1222,11 +1225,11 @@ separate file or a ZIP file.
*/
extern qboolean com_fullyInitialized;

int FS_FOpenFileRead(const char *filename, fileHandle_t *file, qboolean uniqueFILE, module_t module) {
return FS_FOpenFileReadHash(filename, file, uniqueFILE, NULL, module);
int FS_FOpenFileRead(const char *filename, fileHandle_t *file, qboolean uniqueFILE, module_t module, qboolean skipJKA) {
return FS_FOpenFileReadHash(filename, file, uniqueFILE, NULL, module, skipJKA);
}

int FS_FOpenFileReadHash(const char *filename, fileHandle_t *file, qboolean uniqueFILE, unsigned long *filehash, module_t module) {
int FS_FOpenFileReadHash(const char *filename, fileHandle_t *file, qboolean uniqueFILE, unsigned long *filehash, module_t module, qboolean skipJKA) {
bool isLocalConfig;
searchpath_t *search;
char *netpath;
Expand Down Expand Up @@ -1283,6 +1286,10 @@ int FS_FOpenFileReadHash(const char *filename, fileHandle_t *file, qboolean uniq
if ( isLocalConfig && search->pack ) {
continue;
}
// Skip JKA assets if desired
if ( search->pack && search->pack->isJKA && skipJKA ) {
continue;
}
//
if ( search->pack ) {
hash = FS_HashFileName(filename, search->pack->hashSize);
Expand Down Expand Up @@ -1732,7 +1739,7 @@ Filename are relative to the quake search path
a null buffer will just return the file length without loading
============
*/
int FS_ReadFile( const char *qpath, void **buffer ) {
int FS_ReadFile_real( const char *qpath, void **buffer, qboolean skipJKA ) {
fileHandle_t h;
byte* buf;
qboolean isConfig;
Expand Down Expand Up @@ -1794,7 +1801,7 @@ int FS_ReadFile( const char *qpath, void **buffer ) {
}

// look for it in the filesystem or pack files
len = FS_FOpenFileRead( qpath, &h, qfalse );
len = FS_FOpenFileRead( qpath, &h, qfalse, MODULE_MAIN, skipJKA );
if ( h == 0 ) {
if ( buffer ) {
*buffer = NULL;
Expand Down Expand Up @@ -1847,6 +1854,14 @@ int FS_ReadFile( const char *qpath, void **buffer ) {
return len;
}

int FS_ReadFile( const char *qpath, void **buffer ) {
return FS_ReadFile_real( qpath, buffer, qfalse );
}

int FS_ReadFileSkipJKA( const char *qpath, void **buffer ) {
return FS_ReadFile_real( qpath, buffer, qtrue );
}

/*
=============
FS_FreeFile
Expand Down Expand Up @@ -1927,7 +1942,7 @@ Creates a new pak_t in the search chain for the contents
of a zip file.
=================
*/
static pack_t *FS_LoadZipFile( char *zipfile, const char *basename )
static pack_t *FS_LoadZipFile( char *zipfile, const char *basename, qboolean assetsJKA )
{
fileInPack_t *buildBuffer;
pack_t *pack;
Expand All @@ -1942,6 +1957,7 @@ static pack_t *FS_LoadZipFile( char *zipfile, const char *basename )
int fs_numHeaderLongs;
int *fs_headerLongs;
char *namePtr;
int strLength;

fs_numHeaderLongs = 0;

Expand All @@ -1961,7 +1977,17 @@ static pack_t *FS_LoadZipFile( char *zipfile, const char *basename )
if (err != UNZ_OK) {
break;
}
len += strlen(filename_inzip) + 1;
strLength = strlen(filename_inzip);
if ( assetsJKA ) {
// Ugly workaround: rename academy shader files to avoid collisions
if ( strLength > 7 && !Q_stricmp(filename_inzip + strLength - 7, ".shader") ) {
len += strLength + 5; // "_jka" + 1
} else {
len += strlen(filename_inzip) + 1;
Daggolin marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
len += strlen(filename_inzip) + 1;
}
unzGoToNextFile(uf);
}

Expand Down Expand Up @@ -2005,6 +2031,13 @@ static pack_t *FS_LoadZipFile( char *zipfile, const char *basename )
if (file_info.uncompressed_size > 0) {
fs_headerLongs[fs_numHeaderLongs++] = LittleLong(file_info.crc);
}
if ( assetsJKA ) {
// Ugly workaround: rename academy shader files to avoid collisions
strLength = strlen( filename_inzip );
if ( strLength > 7 && !Q_stricmp(filename_inzip + strLength - 7, ".shader") ) {
Q_strcat( filename_inzip, sizeof(filename_inzip), "_jka" );
}
}
Q_strlwr( filename_inzip );
hash = FS_HashFileName(filename_inzip, pack->hashSize);
buildBuffer[i].name = namePtr;
Expand Down Expand Up @@ -2911,7 +2944,7 @@ const char *get_filename(const char *path) {
return slash + 1;
}

static void FS_AddGameDirectory( const char *path, const char *dir, qboolean assetsOnly ) {
static void FS_AddGameDirectory2( const char *path, const char *dir, qboolean assetsOnly, qboolean assetsJKA ) {
searchpath_t *sp;
int i;
searchpath_t *search;
Expand Down Expand Up @@ -2940,7 +2973,7 @@ static void FS_AddGameDirectory( const char *path, const char *dir, qboolean ass
//
// add the directory to the search path
//
if (!assetsOnly) {
if (!assetsOnly && !assetsJKA) {
search = (struct searchpath_s *)Z_Malloc(sizeof(searchpath_t), TAG_FILESYS, qtrue);
search->dir = (directory_t *)Z_Malloc(sizeof(*search->dir), TAG_FILESYS, qtrue);

Expand Down Expand Up @@ -2971,9 +3004,14 @@ static void FS_AddGameDirectory( const char *path, const char *dir, qboolean ass
strcmp(filename, "assets2.pk3") && strcmp(filename, "assets5.pk3")) {
continue;
}
} else if ( assetsJKA ) {
if (strcmp(filename, "assets0.pk3") && strcmp(filename, "assets1.pk3") &&
strcmp(filename, "assets2.pk3") && strcmp(filename, "assets3.pk3")) {
continue;
}
}

if ( ( pak = FS_LoadZipFile( pakfile, pakfiles[i] ) ) == 0 )
if ( ( pak = FS_LoadZipFile( pakfile, pakfiles[i], assetsJKA ) ) == 0 )
continue;

#ifndef DEDICATED
Expand Down Expand Up @@ -3008,6 +3046,9 @@ static void FS_AddGameDirectory( const char *path, const char *dir, qboolean ass
pak->referenced |= FS_GENERAL_REF;
}

// Mark jka assets as such
pak->isJKA = assetsJKA;

search = (searchpath_s *)Z_Malloc (sizeof(searchpath_t), TAG_FILESYS, qtrue);
search->pack = pak;
search->next = fs_searchpaths;
Expand All @@ -3018,6 +3059,10 @@ static void FS_AddGameDirectory( const char *path, const char *dir, qboolean ass
Sys_FreeFileList( pakfiles );
}

static void FS_AddGameDirectory( const char *path, const char *dir, qboolean assetsOnly ) {
Daggolin marked this conversation as resolved.
Show resolved Hide resolved
FS_AddGameDirectory2( path, dir, assetsOnly, qfalse );
}

/*
================
FS_idPak
Expand Down Expand Up @@ -3261,6 +3306,7 @@ FS_Startup
*/
static void FS_Startup( const char *gameName ) {
const char *assetsPath;
const char *assetsPathJKA;
// Silence clang, they do get initialized
char *mv_whitelist = NULL, *mv_blacklist = NULL, *mv_forcelist = NULL;
fileHandle_t f_w, f_b, f_f;
Expand All @@ -3281,6 +3327,10 @@ static void FS_Startup( const char *gameName ) {
assetsPath = Sys_DefaultAssetsPath();
fs_assetspath = Cvar_Get("fs_assetspath", assetsPath ? assetsPath : "", CVAR_INIT | CVAR_VM_NOWRITE);

assetsPathJKA = Sys_DefaultAssetsPathJKA();
fs_assetspathJKA = Cvar_Get("fs_assetspathJKA", assetsPathJKA ? assetsPathJKA : "", CVAR_INIT | CVAR_VM_NOWRITE);
Daggolin marked this conversation as resolved.
Show resolved Hide resolved
Daggolin marked this conversation as resolved.
Show resolved Hide resolved
fs_noJKA = Cvar_Get("fs_noJKA", "0", CVAR_ARCHIVE);
Daggolin marked this conversation as resolved.
Show resolved Hide resolved

if (!FS_AllPath_Base_FileExists("assets5.pk3")) {
// assets files found in none of the paths
#if defined(INSTALLED)
Expand All @@ -3297,9 +3347,14 @@ static void FS_Startup( const char *gameName ) {
return;
}

// Try to load JKA assets if a path has been specified
if (fs_assetspathJKA->string[0] && !fs_noJKA->integer) {
FS_AddGameDirectory2(fs_assetspathJKA->string, BASEGAME, qfalse, qtrue);
}

// don't use the assetspath if assets files already found in fs_basepath or fs_homepath
if (assetsPath && !FS_BaseHome_Base_FileExists("assets5.pk3")) {
FS_AddGameDirectory(assetsPath, BASEGAME, qtrue);
if (fs_assetspath->string[0] && !FS_BaseHome_Base_FileExists("assets5.pk3")) {
FS_AddGameDirectory(fs_assetspath->string, BASEGAME, qtrue);
}

// add search path elements in reverse priority order
Expand Down Expand Up @@ -3787,6 +3842,8 @@ void FS_InitFilesystem( void ) {
// has already been initialized
Com_StartupVariable( "fs_basepath" );
Com_StartupVariable( "fs_homepath" );
Com_StartupVariable( "fs_assetspathJKA" );
Com_StartupVariable( "fs_noJKA" );
#if !defined(PORTABLE)
Com_StartupVariable( "fs_assetspath" );
#endif
Expand Down
5 changes: 3 additions & 2 deletions src/qcommon/qcommon.h
Expand Up @@ -640,8 +640,8 @@ fileHandle_t FS_SV_FOpenFileWrite( const char *filename, module_t module = MODUL
fileHandle_t FS_SV_FOpenFileAppend( const char *filename, module_t module = MODULE_MAIN );
int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp, module_t module = MODULE_MAIN );
void FS_SV_Rename( const char *from, const char *to );
int FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE, module_t module = MODULE_MAIN );
int FS_FOpenFileReadHash( const char *filename, fileHandle_t *file, qboolean uniqueFILE, unsigned long *filehash, module_t module = MODULE_MAIN );
int FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE, module_t module = MODULE_MAIN, qboolean skipJKA = qfalse );
int FS_FOpenFileReadHash( const char *filename, fileHandle_t *file, qboolean uniqueFILE, unsigned long *filehash, module_t module = MODULE_MAIN, qboolean skipJKA = qfalse );
// if uniqueFILE is true, then a new FILE will be fopened even if the file
// is found in an already open pak file. If uniqueFILE is false, you must call
// FS_FCloseFile instead of fclose, otherwise the pak FILE would be improperly closed
Expand All @@ -667,6 +667,7 @@ int FS_ReadFile( const char *qpath, void **buffer );
// A 0 byte will always be appended at the end, so string ops are safe.
// the buffer should be considered read-only, because it may be cached
// for other uses.
int FS_ReadFileSkipJKA( const char *qpath, void **buffer );

void FS_ForceFlush( fileHandle_t f, module_t module = MODULE_MAIN );
// forces flush on files we're writing to.
Expand Down
51 changes: 40 additions & 11 deletions src/renderer/tr_image.cpp
Expand Up @@ -22,8 +22,8 @@ using namespace std;
#include <jpeglib.h>
#include <png.h>

static void LoadTGA( const char *name, byte **pic, int *width, int *height );
static void LoadJPG( const char *name, byte **pic, int *width, int *height );
static void LoadTGA( const char *name, byte **pic, int *width, int *height, qboolean skipJKA );
static void LoadJPG( const char *name, byte **pic, int *width, int *height, qboolean skipJKA );

static byte s_intensitytable[256];
static unsigned char s_gammatable[256];
Expand Down Expand Up @@ -1205,7 +1205,7 @@ typedef struct
// returns false if found but had a format error, else true for either OK or not-found (there's a reason for this)
//

void LoadTGA ( const char *name, byte **pic, int *width, int *height)
void LoadTGA ( const char *name, byte **pic, int *width, int *height, qboolean skipJKA)
{
char sErrorString[1024];
bool bFormatErrors = false;
Expand All @@ -1226,7 +1226,8 @@ void LoadTGA ( const char *name, byte **pic, int *width, int *height)
// load the file
//
byte *pTempLoadedBuffer = 0;
ri.FS_ReadFile ( name, (void **)&pTempLoadedBuffer);
if ( skipJKA ) ri.FS_ReadFileSkipJKA( name, (void**)&pTempLoadedBuffer );
else ri.FS_ReadFile ( name, (void **)&pTempLoadedBuffer);
if (!pTempLoadedBuffer) {
return;
}
Expand Down Expand Up @@ -1556,7 +1557,7 @@ static void R_JPGOutputMessage( j_common_ptr cinfo )
Com_Printf("%s\n", buffer);
}

void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height ) {
void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height, qboolean skipJKA ) {
/* This struct contains the JPEG decompression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
*/
Expand Down Expand Up @@ -1591,7 +1592,9 @@ void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height
* requires it in order to read binary files.
*/

int len = ri.FS_ReadFile ( filename, &fbuffer.v);
int len;
if ( skipJKA ) len = ri.FS_ReadFileSkipJKA ( filename, &fbuffer.v );
else len = ri.FS_ReadFile ( filename, &fbuffer.v);
if (!fbuffer.b || len < 0) {
return;
}
Expand Down Expand Up @@ -2093,10 +2096,12 @@ void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
}

// Loads a PNG image from file.
void LoadPNG(const char *filename, byte **data, int *width, int *height)
void LoadPNG(const char *filename, byte **data, int *width, int *height, qboolean skipJKA)
{
char *buf = NULL;
int len = ri.FS_ReadFile(filename, (void **)&buf);
int len;
if ( skipJKA ) len = ri.FS_ReadFileSkipJKA(filename, (void**)&buf);
else len = ri.FS_ReadFile(filename, (void **)&buf);
if (len < 0 || buf == NULL)
{
return;
Expand Down Expand Up @@ -2125,23 +2130,47 @@ void R_LoadImage( const char *shortname, byte **pic, int *width, int *height )
*width = 0;
*height = 0;

// First try without JKA assets
COM_StripExtension(shortname,name,sizeof(name));
COM_DefaultExtension(name, sizeof(name), ".jpg");
LoadJPG( name, pic, width, height );
LoadJPG( name, pic, width, height, qtrue );
if (*pic) {
return;
}

COM_StripExtension(shortname,name,sizeof(name));
COM_DefaultExtension(name, sizeof(name), ".png");
LoadPNG( name, pic, width, height ); // try png first
LoadPNG( name, pic, width, height, qtrue ); // try png first
if (*pic){
return;
}

COM_StripExtension(shortname,name,sizeof(name));
COM_DefaultExtension(name, sizeof(name), ".tga");
LoadTGA( name, pic, width, height ); // try tga first
LoadTGA( name, pic, width, height, qtrue ); // try tga first
if (*pic){
return;
}


// Retry with JKA assets
COM_StripExtension(shortname,name,sizeof(name));
COM_DefaultExtension(name, sizeof(name), ".jpg");
LoadJPG( name, pic, width, height, qfalse );
if (*pic) {
return;
}

COM_StripExtension(shortname,name,sizeof(name));
COM_DefaultExtension(name, sizeof(name), ".png");
LoadPNG( name, pic, width, height, qfalse ); // try png first
if (*pic){
return;
}

COM_StripExtension(shortname,name,sizeof(name));
COM_DefaultExtension(name, sizeof(name), ".tga");
LoadTGA( name, pic, width, height, qfalse ); // try tga first
if (*pic){
return;
}
Expand Down