/* * Copyright (c) 2023 Vera Visions LLC. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ typedef struct { string name; string title; string author; string type; string preview; } mapLibrary_t; mapLibrary_t *g_mapLibrary; var int g_mapLibrary_count = 0i; var int map_blacklist_count = 0i; string *map_blacklist; static string MapLibrary_GetMapGamedir(void) { string gdir = cvar_string("fs_game"); /* HACK: work around FTEQW's path choice */ if (gdir == "ftehl") gdir = "valve"; return gdir; } static bool MapLibrary_MapInGameDir(string fileName, string gameDir) { bool list = true; /* only look for maps in the current gamedir, requires SEARCH_FULLPACKAGE */ if (gameDir != MapLibrary_GetMapGamedir()) { return false; } /* ignore test_ prefix maps */ if (substring(fileName, 0, 5) == "test_") { return false; } /* see if any of our blacklisted names match */ for (int b = 0i; b < map_blacklist_count; b++) { if (fileName == map_blacklist[b]) { list = false; break; } } return list; } static string MapLibrary_FindPreview(string mapFile) { string imageExtensions = strcat(cvar_string("r_imageextensions"), " mat"); int imageFormats = (int)tokenize(imageExtensions); string previewFile = ""; string mapName = substring(mapFile, 0, strlen(mapFile) - 4); /* cycle through all possible extensions */ for (int i = 0; i < imageFormats; i++) { previewFile = strcat("levelshots/", mapName, ".", argv(i)); if (whichpack(previewFile)) { return previewFile; } previewFile = strcat("maps/", mapName, ".", argv(i)); if (whichpack(previewFile)) { return previewFile; } } return __NULL__; } void MapLibrary_Init(void) { int i = 0i; int c = 0i; int mapCount = 0i; string mapFile, mapDir; searchhandle mapsearch; string gameDir; /* already initialized */ if (g_mapLibrary_count != 0i) return; g_mapLibrary_count = 0i; gameDir = MapLibrary_GetMapGamedir(); /* map blacklist code */ filestream fs_blacklist; fs_blacklist = fopen("scripts/map_blacklist", FILE_READ); if (fs_blacklist >= 0) { string temp; while ((temp = fgets(fs_blacklist))) { map_blacklist_count++; } map_blacklist = memalloc(sizeof(string) * map_blacklist_count); fseek(fs_blacklist, 0); while ((temp = fgets(fs_blacklist))) { map_blacklist[i] = temp; i++; } fclose(fs_blacklist); } /* search for all maps in the current PATH */ mapsearch = search_begin("maps/*.bsp", SEARCH_NAMESORT | SEARCH_FULLPACKAGE, TRUE); mapCount = search_getsize(mapsearch); /* we now iterate over the search entries to filter results out */ for (i = 0i; i < mapCount; i++) { mapFile = substring(search_getfilename(mapsearch, i), 5, -1); /* the strlen(gameDir) is technically wrong, but it'll work anyway because we only care about maps in the current game directory */ mapDir = substring(search_getpackagename(mapsearch, i), 0, strlen(gameDir)); if (MapLibrary_MapInGameDir(mapFile, mapDir) == true) { g_mapLibrary_count++; } } search_end(mapsearch); g_mapLibrary = (mapLibrary_t *)memalloc(sizeof(mapLibrary_t) * g_mapLibrary_count); /* let's do it again, but this time we'll sort the data into our array */ mapsearch = search_begin("maps/*.bsp", SEARCH_NAMESORT | SEARCH_FULLPACKAGE, TRUE); mapCount = search_getsize(mapsearch); for (i = 0i; i < mapCount; i++) { mapFile = substring(search_getfilename(mapsearch, i), 5, -1); mapDir = substring(search_getpackagename(mapsearch, i), 0, strlen(gameDir)); if (MapLibrary_MapInGameDir(mapFile, mapDir) == true) { g_mapLibrary[c].name = mapFile; g_mapLibrary[c].title = mapFile; g_mapLibrary[c].author = "Unknown"; g_mapLibrary[c].type = "Unknown"; g_mapLibrary[c].preview = MapLibrary_FindPreview(mapFile); c++; } } search_end(mapsearch); NSLog("...MapLibrary initialized (%i entries).", g_mapLibrary_count); } int MapLibrary_GetMapCount(void) { return g_mapLibrary_count; } __variant MapLibrary_GetInfo(int mapID, mapType_t infoType) { if (mapID >= g_mapLibrary_count || mapID < 0i) { NSError("Invalid map id %i", mapID); return __NULL__; } switch (infoType) { case MAPINFO_NAME: return g_mapLibrary[mapID].name; break; case MAPINFO_TITLE: return g_mapLibrary[mapID].title; break; case MAPINFO_AUTHOR: return g_mapLibrary[mapID].author; break; case MAPINFO_TYPE: return g_mapLibrary[mapID].type; break; case MAPINFO_PREVIEW: return g_mapLibrary[mapID].preview; break; default: return __NULL__; } }