filesystem: sort files in pack and use binary search in packs

This commit is contained in:
Denis Pauk 2024-04-07 12:51:00 +03:00
parent ae4845782c
commit 30861e07ef

View file

@ -353,6 +353,54 @@ FS_FCloseFile(fileHandle_t f)
memset(handle, 0, sizeof(*handle));
}
static int
FS_SortPackCompare(const void *p1, const void *p2)
{
fsPackFile_t *file1, *file2;
file1 = (fsPackFile_t*)p1;
file2 = (fsPackFile_t*)p2;
return Q_stricmp(file1->name, file2->name);
}
static void
FS_SortPack(fsPack_t *pak)
{
qsort(pak->files, pak->numFiles, sizeof(fsPackFile_t), FS_SortPackCompare);
}
static int
FS_PackQuickSearch(const fsPack_t *pak, const char *name)
{
int start, end;
start = 0;
end = pak->numFiles;
while (start <= end)
{
int i, res;
i = start + (end - start) / 2;
res = Q_stricmp(pak->files[i].name, name);
if (res == 0)
{
return i;
}
else if (res < 0)
{
start = i + 1;
}
else
{
end = i - 1;
}
}
return -1;
}
/*
* Finds the file in the search path. Returns filesize and an open FILE *. Used
* for streaming data out of either a pak file or a seperate file.
@ -364,14 +412,14 @@ FS_FOpenFile(const char *rawname, fileHandle_t *f, qboolean gamedir_only)
fsHandle_t *handle;
fsPack_t *pack;
fsSearchPath_t *search;
int i;
int input, output;
// Remove self references and empty dirs from the requested path.
// ZIPs and PAKs don't support them, but they may be hardcoded in
// some custom maps or models.
char name[MAX_QPATH] = {0};
size_t namelen = strlen(rawname);
for (int input = 0, output = 0; input < namelen; input++)
for (input = 0, output = 0; input < namelen; input++)
{
// Remove self reference.
if (rawname[input] == '.')
@ -448,74 +496,74 @@ FS_FOpenFile(const char *rawname, fileHandle_t *f, qboolean gamedir_only)
/* Search inside a pack file. */
if (search->pack)
{
int i;
pack = search->pack;
i = FS_PackQuickSearch(pack, handle->name);
for (i = 0; i < pack->numFiles; i++)
if (i >= 0)
{
if (Q_stricmp(pack->files[i].name, handle->name) == 0)
/* Found it! */
if (fs_debug->value)
{
/* Found it! */
if (fs_debug->value)
Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n",
handle->name, pack->name);
}
// save the name with *correct case* in the handle
// (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)
Q_strlcpy(handle->name, pack->files[i].name, sizeof(handle->name));
handle->compressed_size = 0;
handle->format = PAK_MODE_Q2;
if (pack->pak)
{
/* PAK and DAT */
if (pack->isProtectedPak)
{
Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n",
handle->name, pack->name);
file_from_protected_pak = true;
}
// save the name with *correct case* in the handle
// (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)
Q_strlcpy(handle->name, pack->files[i].name, sizeof(handle->name));
handle->compressed_size = 0;
handle->format = PAK_MODE_Q2;
handle->file = Q_fopen(pack->name, "rb");
if (pack->pak)
if (handle->file)
{
/* PAK and DAT */
if (pack->isProtectedPak)
{
file_from_protected_pak = true;
}
handle->file = Q_fopen(pack->name, "rb");
if (handle->file)
{
handle->compressed_size = pack->files[i].compressed_size;
handle->format = pack->files[i].format;
fseek(handle->file, pack->files[i].offset, SEEK_SET);
return pack->files[i].size;
}
handle->compressed_size = pack->files[i].compressed_size;
handle->format = pack->files[i].format;
fseek(handle->file, pack->files[i].offset, SEEK_SET);
return pack->files[i].size;
}
else if (pack->pk3)
}
else if (pack->pk3)
{
/* PK3 */
if (pack->isProtectedPak)
{
/* PK3 */
if (pack->isProtectedPak)
{
file_from_protected_pak = true;
}
file_from_protected_pak = true;
}
#ifdef _WIN32
handle->zip = unzOpen2(pack->name, &zlib_file_api);
handle->zip = unzOpen2(pack->name, &zlib_file_api);
#else
handle->zip = unzOpen(pack->name);
handle->zip = unzOpen(pack->name);
#endif
if (handle->zip)
if (handle->zip)
{
if (unzLocateFile(handle->zip, handle->name, 2) == UNZ_OK)
{
if (unzLocateFile(handle->zip, handle->name, 2) == UNZ_OK)
if (unzOpenCurrentFile(handle->zip) == UNZ_OK)
{
if (unzOpenCurrentFile(handle->zip) == UNZ_OK)
{
return pack->files[i].size;
}
return pack->files[i].size;
}
unzClose(handle->zip);
}
}
Com_Error(ERR_FATAL, "Couldn't reopen '%s'", pack->name);
unzClose(handle->zip);
}
}
Com_Error(ERR_FATAL, "Couldn't reopen '%s'", pack->name);
}
}
else
@ -1301,8 +1349,10 @@ FS_LoadPAK(const char *packPath)
}
fclose(handle);
Com_Printf("WARNING: skipped unsupported '%s' PAK format.\n",
packPath);
return NULL;
}
@ -2058,8 +2108,12 @@ FS_AddPAKFromGamedir(const char *pak)
}
else
{
fsSearchPath_t *search;
FS_SortPack(pakfile);
// Add it.
fsSearchPath_t *search = Z_Malloc(sizeof(fsSearchPath_t));
search = Z_Malloc(sizeof(fsSearchPath_t));
search->pack = pakfile;
search->next = fs_searchPaths;
fs_searchPaths = search;
@ -2204,6 +2258,8 @@ FS_AddDirToSearchPath(char *dir, qboolean create) {
continue;
}
FS_SortPack(pack);
search = Z_Malloc(sizeof(fsSearchPath_t));
search->pack = pack;
search->next = fs_searchPaths;
@ -2278,6 +2334,8 @@ FS_AddDirToSearchPath(char *dir, qboolean create) {
continue;
}
FS_SortPack(pack);
pack->isProtectedPak = false;
search = Z_Malloc(sizeof(fsSearchPath_t));