mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2025-02-17 01:21:12 +00:00
filesystem: add support of Anachronox DAT archive format
This commit is contained in:
parent
c385789a87
commit
04807e2ece
2 changed files with 196 additions and 7 deletions
|
@ -51,6 +51,7 @@ typedef struct
|
||||||
fsMode_t mode;
|
fsMode_t mode;
|
||||||
FILE *file; /* Only one will be used. */
|
FILE *file; /* Only one will be used. */
|
||||||
unzFile *zip; /* (file or zip) */
|
unzFile *zip; /* (file or zip) */
|
||||||
|
int compressed_size; /* Should be zero for original PAK files */
|
||||||
} fsHandle_t;
|
} fsHandle_t;
|
||||||
|
|
||||||
typedef struct fsLink_s
|
typedef struct fsLink_s
|
||||||
|
@ -66,6 +67,7 @@ typedef struct
|
||||||
char name[MAX_QPATH];
|
char name[MAX_QPATH];
|
||||||
int size;
|
int size;
|
||||||
int offset; /* Ignored in PK3 files. */
|
int offset; /* Ignored in PK3 files. */
|
||||||
|
int compressed_size; /* Should be zero for original PAK files */
|
||||||
} fsPackFile_t;
|
} fsPackFile_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -87,6 +89,7 @@ typedef struct fsSearchPath_s
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
|
DAT,
|
||||||
PAK,
|
PAK,
|
||||||
PK3
|
PK3
|
||||||
} fsPackFormat_t;
|
} fsPackFormat_t;
|
||||||
|
@ -104,6 +107,7 @@ fsSearchPath_t *fs_baseSearchPaths = NULL;
|
||||||
|
|
||||||
/* Pack formats / suffixes. */
|
/* Pack formats / suffixes. */
|
||||||
fsPackTypes_t fs_packtypes[] = {
|
fsPackTypes_t fs_packtypes[] = {
|
||||||
|
{"dat", DAT},
|
||||||
{"pak", PAK},
|
{"pak", PAK},
|
||||||
{"pk2", PK3},
|
{"pk2", PK3},
|
||||||
{"pk3", PK3},
|
{"pk3", PK3},
|
||||||
|
@ -449,10 +453,11 @@ FS_FOpenFile(const char *rawname, fileHandle_t *f, qboolean gamedir_only)
|
||||||
// (relevant for savegames, when starting map with wrong case but it's still found
|
// (relevant for savegames, when starting map with wrong case but it's still found
|
||||||
// because it's from pak, but save/bla/MAPname.sav/sv2 will have wrong case and can't be found then)
|
// because it's from pak, but save/bla/MAPname.sav/sv2 will have wrong case and can't be found then)
|
||||||
Q_strlcpy(handle->name, pack->files[i].name, sizeof(handle->name));
|
Q_strlcpy(handle->name, pack->files[i].name, sizeof(handle->name));
|
||||||
|
handle->compressed_size = 0;
|
||||||
|
|
||||||
if (pack->pak)
|
if (pack->pak)
|
||||||
{
|
{
|
||||||
/* PAK */
|
/* PAK and DAT */
|
||||||
if (pack->isProtectedPak)
|
if (pack->isProtectedPak)
|
||||||
{
|
{
|
||||||
file_from_protected_pak = true;
|
file_from_protected_pak = true;
|
||||||
|
@ -462,6 +467,7 @@ FS_FOpenFile(const char *rawname, fileHandle_t *f, qboolean gamedir_only)
|
||||||
|
|
||||||
if (handle->file)
|
if (handle->file)
|
||||||
{
|
{
|
||||||
|
handle->compressed_size = pack->files[i].compressed_size;
|
||||||
fseek(handle->file, pack->files[i].offset, SEEK_SET);
|
fseek(handle->file, pack->files[i].offset, SEEK_SET);
|
||||||
return pack->files[i].size;
|
return pack->files[i].size;
|
||||||
}
|
}
|
||||||
|
@ -536,6 +542,34 @@ FS_FOpenFile(const char *rawname, fileHandle_t *f, qboolean gamedir_only)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
FS_DecompressFile(void *buffer, int size, fsHandle_t *handle)
|
||||||
|
{
|
||||||
|
if (handle->compressed_size)
|
||||||
|
{
|
||||||
|
unsigned long uncomressed_size;
|
||||||
|
byte *comressed_buffer;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
comressed_buffer = malloc(handle->compressed_size);
|
||||||
|
uncomressed_size = size;
|
||||||
|
|
||||||
|
memcpy(comressed_buffer, buffer, handle->compressed_size);
|
||||||
|
|
||||||
|
status = uncompress(buffer, &uncomressed_size, comressed_buffer, handle->compressed_size);
|
||||||
|
|
||||||
|
free(comressed_buffer);
|
||||||
|
|
||||||
|
if (status != MZ_OK)
|
||||||
|
{
|
||||||
|
Com_Error(ERR_FATAL, "%s: can't decompress file '%s'", __func__, handle->name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Properly handles partial reads.
|
* Properly handles partial reads.
|
||||||
*/
|
*/
|
||||||
|
@ -551,7 +585,14 @@ FS_Read(void *buffer, int size, fileHandle_t f)
|
||||||
handle = FS_GetFileByHandle(f);
|
handle = FS_GetFileByHandle(f);
|
||||||
|
|
||||||
/* Read. */
|
/* Read. */
|
||||||
remaining = size;
|
if (handle->compressed_size)
|
||||||
|
{
|
||||||
|
remaining = handle->compressed_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remaining = size;
|
||||||
|
}
|
||||||
buf = (byte *)buffer;
|
buf = (byte *)buffer;
|
||||||
|
|
||||||
while (remaining)
|
while (remaining)
|
||||||
|
@ -592,7 +633,7 @@ FS_Read(void *buffer, int size, fileHandle_t f)
|
||||||
buf += r;
|
buf += r;
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
return FS_DecompressFile(buffer, size, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -618,7 +659,14 @@ FS_FRead(void *buffer, int size, int count, fileHandle_t f)
|
||||||
while (loops)
|
while (loops)
|
||||||
{
|
{
|
||||||
/* Read in chunks. */
|
/* Read in chunks. */
|
||||||
remaining = size;
|
if (handle->compressed_size)
|
||||||
|
{
|
||||||
|
remaining = handle->compressed_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remaining = size;
|
||||||
|
}
|
||||||
|
|
||||||
while (remaining)
|
while (remaining)
|
||||||
{
|
{
|
||||||
|
@ -651,8 +699,8 @@ FS_FRead(void *buffer, int size, int count, fileHandle_t f)
|
||||||
else if (r == -1)
|
else if (r == -1)
|
||||||
{
|
{
|
||||||
Com_Error(ERR_FATAL,
|
Com_Error(ERR_FATAL,
|
||||||
"FS_FRead: -1 bytes read from '%s'",
|
"%s: -1 bytes read from '%s'",
|
||||||
handle->name);
|
__func__, handle->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
remaining -= r;
|
remaining -= r;
|
||||||
|
@ -662,7 +710,7 @@ FS_FRead(void *buffer, int size, int count, fileHandle_t f)
|
||||||
loops--;
|
loops--;
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
return FS_DecompressFile(buffer, size, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -764,6 +812,110 @@ FS_FreeSearchPaths(fsSearchPath_t *start, fsSearchPath_t *end)
|
||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Takes an explicit (not game tree related) path to a pak file.
|
||||||
|
*
|
||||||
|
* Loads the header and directory, adding the files at the beginning of the
|
||||||
|
* list so they override previous pack files.
|
||||||
|
*/
|
||||||
|
static fsPack_t *
|
||||||
|
FS_LoadDAT(const char *packPath)
|
||||||
|
{
|
||||||
|
int i; /* Loop counter. */
|
||||||
|
int numFiles; /* Number of files in DAT. */
|
||||||
|
FILE *handle; /* File handle. */
|
||||||
|
fsPackFile_t *files; /* List of files in DAT. */
|
||||||
|
fsPack_t *pack; /* DAT file. */
|
||||||
|
ddatheader_t header; /* DAT file header. */
|
||||||
|
ddatfile_t *info = NULL; /* DAT info. */
|
||||||
|
const char *prefixpos, *pos;
|
||||||
|
char prefix[MAX_QPATH];
|
||||||
|
|
||||||
|
handle = Q_fopen(packPath, "rb");
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(&header, 1, sizeof(ddatheader_t), handle);
|
||||||
|
|
||||||
|
if (LittleLong(header.ident) != IDDATHEADER ||
|
||||||
|
LittleLong(header.version) != IDDATVERSION)
|
||||||
|
{
|
||||||
|
fclose(handle);
|
||||||
|
Com_Error(ERR_FATAL, "%s: '%s' is not a dat file", __func__, packPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
header.dirofs = LittleLong(header.dirofs);
|
||||||
|
header.dirlen = LittleLong(header.dirlen);
|
||||||
|
|
||||||
|
numFiles = header.dirlen / sizeof(ddatfile_t);
|
||||||
|
|
||||||
|
if ((numFiles == 0) || (header.dirlen < 0) || (header.dirofs < 0))
|
||||||
|
{
|
||||||
|
fclose(handle);
|
||||||
|
Com_Error(ERR_FATAL, "%s: '%s' is too short.",
|
||||||
|
__func__, packPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numFiles > MAX_FILES_IN_PACK)
|
||||||
|
{
|
||||||
|
Com_Printf("%s: '%s' has %i > %i files\n",
|
||||||
|
__func__, packPath, numFiles, MAX_FILES_IN_PACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
info = malloc(header.dirlen);
|
||||||
|
if (!info)
|
||||||
|
{
|
||||||
|
Com_Error(ERR_FATAL, "%s: '%s' is to big for read %d",
|
||||||
|
__func__, packPath, header.dirlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
files = Z_Malloc(numFiles * sizeof(fsPackFile_t));
|
||||||
|
|
||||||
|
fseek(handle, header.dirofs, SEEK_SET);
|
||||||
|
fread(info, 1, header.dirlen, handle);
|
||||||
|
|
||||||
|
prefixpos = pos = packPath;
|
||||||
|
while (*pos)
|
||||||
|
{
|
||||||
|
if (*pos == '\\' || *pos == '/')
|
||||||
|
{
|
||||||
|
prefixpos = pos;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* '/' + '.dat' */
|
||||||
|
memset(prefix, 0, sizeof(prefix));
|
||||||
|
strncpy(prefix, prefixpos + 1,
|
||||||
|
Q_min(strlen(prefixpos) - 5, sizeof(prefix) - 1));
|
||||||
|
|
||||||
|
/* Parse the directory. */
|
||||||
|
for (i = 0; i < numFiles; i++)
|
||||||
|
{
|
||||||
|
Q_strlcpy(files[i].name, prefix, sizeof(files[i].name));
|
||||||
|
strncat(files[i].name, "/", sizeof(files[i].name) - 1);
|
||||||
|
strncat(files[i].name, info[i].name, sizeof(files[i].name) - i);
|
||||||
|
files[i].offset = LittleLong(info[i].filepos);
|
||||||
|
files[i].size = LittleLong(info[i].filelen);
|
||||||
|
files[i].compressed_size = LittleLong(info[i].compressedlen);
|
||||||
|
}
|
||||||
|
free(info);
|
||||||
|
|
||||||
|
pack = Z_Malloc(sizeof(fsPack_t));
|
||||||
|
Q_strlcpy(pack->name, packPath, sizeof(pack->name));
|
||||||
|
pack->pak = handle;
|
||||||
|
pack->pk3 = NULL;
|
||||||
|
pack->numFiles = numFiles;
|
||||||
|
pack->files = files;
|
||||||
|
|
||||||
|
Com_Printf("Added datfile '%s' (%i files).\n", pack->name, numFiles);
|
||||||
|
|
||||||
|
return pack;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Takes an explicit (not game tree related) path to a pak file.
|
* Takes an explicit (not game tree related) path to a pak file.
|
||||||
*
|
*
|
||||||
|
@ -832,6 +984,7 @@ FS_LoadPAK(const char *packPath)
|
||||||
Q_strlcpy(files[i].name, info[i].name, sizeof(files[i].name));
|
Q_strlcpy(files[i].name, info[i].name, sizeof(files[i].name));
|
||||||
files[i].offset = LittleLong(info[i].filepos);
|
files[i].offset = LittleLong(info[i].filepos);
|
||||||
files[i].size = LittleLong(info[i].filelen);
|
files[i].size = LittleLong(info[i].filelen);
|
||||||
|
files[i].compressed_size = 0;
|
||||||
}
|
}
|
||||||
free(info);
|
free(info);
|
||||||
|
|
||||||
|
@ -1578,6 +1731,9 @@ FS_AddPAKFromGamedir(const char *pak)
|
||||||
|
|
||||||
switch (fs_packtypes[i].format)
|
switch (fs_packtypes[i].format)
|
||||||
{
|
{
|
||||||
|
case DAT:
|
||||||
|
pakfile = FS_LoadDAT(path);
|
||||||
|
break;
|
||||||
case PAK:
|
case PAK:
|
||||||
pakfile = FS_LoadPAK(path);
|
pakfile = FS_LoadPAK(path);
|
||||||
break;
|
break;
|
||||||
|
@ -1696,6 +1852,14 @@ FS_AddDirToSearchPath(char *dir, qboolean create) {
|
||||||
|
|
||||||
switch (fs_packtypes[i].format)
|
switch (fs_packtypes[i].format)
|
||||||
{
|
{
|
||||||
|
case DAT:
|
||||||
|
pack = FS_LoadDAT(path);
|
||||||
|
|
||||||
|
if (pack)
|
||||||
|
{
|
||||||
|
pack->isProtectedPak = true;
|
||||||
|
}
|
||||||
|
|
||||||
case PAK:
|
case PAK:
|
||||||
pack = FS_LoadPAK(path);
|
pack = FS_LoadPAK(path);
|
||||||
|
|
||||||
|
@ -1776,6 +1940,9 @@ FS_AddDirToSearchPath(char *dir, qboolean create) {
|
||||||
|
|
||||||
switch (fs_packtypes[i].format)
|
switch (fs_packtypes[i].format)
|
||||||
{
|
{
|
||||||
|
case DAT:
|
||||||
|
pack = FS_LoadDAT(list[j]);
|
||||||
|
break;
|
||||||
case PAK:
|
case PAK:
|
||||||
pack = FS_LoadPAK(list[j]);
|
pack = FS_LoadPAK(list[j]);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -27,6 +27,28 @@
|
||||||
#ifndef CO_FILES_H
|
#ifndef CO_FILES_H
|
||||||
#define CO_FILES_H
|
#define CO_FILES_H
|
||||||
|
|
||||||
|
/* The .dat files are just a linear collapse of a directory tree */
|
||||||
|
|
||||||
|
#define IDDATHEADER (('T' << 24) + ('A' << 16) + ('D' << 8) + 'A')
|
||||||
|
#define IDDATVERSION 9
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char name[128];
|
||||||
|
int filepos;
|
||||||
|
int filelen;
|
||||||
|
int compressedlen;
|
||||||
|
int checksum;
|
||||||
|
} ddatfile_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int ident; /* == IDDATHEADER */
|
||||||
|
int dirofs;
|
||||||
|
int dirlen;
|
||||||
|
int version; /* == IDPAKVERSION */
|
||||||
|
} ddatheader_t;
|
||||||
|
|
||||||
/* The .pak files are just a linear collapse of a directory tree */
|
/* The .pak files are just a linear collapse of a directory tree */
|
||||||
|
|
||||||
#define IDPAKHEADER (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P')
|
#define IDPAKHEADER (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P')
|
||||||
|
|
Loading…
Reference in a new issue