Files
astromaddie--HaloCE-VR/source/cache/cache_files.c
T
Madison 7d96cc32ec Don't free a null tag table on the first level load
cache_file_add_tag freed its last copy of the tag table before making one,
and on the first map with a pause menu (the first level; the main menu has
none) that copy is NULL. The game's free (match_free) halts on a null
pointer, so loading any level from the main menu stopped at the blue
error screen once vr_menu_tags_loaded added the VR settings. On the device
a10 now loads, the VR settings join the pause menu and the opening
cutscene plays (not yet worn past it).
2026-10-03 12:48:03 +09:00

1012 lines
25 KiB
C

/*
CACHE_FILES.C
symbols in this file:
001A9180 00f0:
_cache_files_map_directory (0000)
001A9270 0030:
_scenario_tags_unload (0000)
001A92A0 0010:
_tag_files_open (0000)
001A92B0 0010:
_tag_files_close (0000)
001A92C0 0040:
_tag_groups_checksum (0000)
001A9300 0010:
_cache_files_get_checksum (0000)
001A9310 00b0:
_tag_loaded (0000)
001A93C0 0020:
_cache_files_enable_writes (0000)
001A93E0 0090:
_cache_files_disable_writes (0000)
001A9470 0020:
_tag_block_resize (0000)
001A9490 0020:
_tag_data_resize (0000)
001A94B0 0020:
_tag_block_add_element (0000)
001A94D0 0010:
_tag_block_delete_element (0000)
001A94E0 0020:
_tag_load (0000)
001A9500 0010:
_tag_unload (0000)
001A9510 0020:
_tag_file_get_path (0000)
001A9530 0010:
_tag_reference_set (0000)
001A9540 0020:
_tag_iterator_new (0000)
001A9560 0070:
_tag_iterator_next (0000)
001A95D0 00f0:
_cache_get_tag_instance (0000)
001A96C0 0100:
_cache_file_header_verify (0000)
001A97C0 0090:
_cache_files_give_time_to_precache (0000)
001A9850 0130:
_scenario_tags_load (0000)
001A9980 0120:
_scenario_structure_bsp_load (0000)
001A9AA0 0080:
_scenario_structure_bsp_unload (0000)
001A9B20 00b0:
_tag_get (0000)
001A9BD0 0020:
_tag_get_name (0000)
001A9BF0 0020:
_tag_get_group_tag (0000)
002A62D8 0009:
??_C@_08NDLPNBDL@d?3?2maps?2?$AA@ (0000)
002A62E4 000c:
??_C@_0M@KPLLEAGM@d?3?2maps_it?2?$AA@ (0000)
002A62F0 000c:
??_C@_0M@OACCOJFB@d?3?2maps_es?2?$AA@ (0000)
002A62FC 000c:
??_C@_0M@PDFFCMII@d?3?2maps_fr?2?$AA@ (0000)
002A6308 000c:
??_C@_0M@EADFFBPG@d?3?2maps_de?2?$AA@ (0000)
002A6314 001e:
??_C@_0BO@JHMCCGLN@no?5valid?5map?5directory?5exists?$AA@ (0000)
002A6334 0023:
??_C@_0CD@DIDKODIK@c?3?2halo?2SOURCE?2cache?2cache_files@ (0000)
002A6358 001f:
??_C@_0BP@GJKOKCFG@cache_file_globals?4tags_loaded?$AA@ (0000)
002A6378 0015:
??_C@_0BF@KAIMLJJI@global_tag_instances?$AA@ (0000)
002A6390 003d:
??_C@_0DN@EPHHKDMF@tag_block_resize?$CI?$CJ?5is?5not?5suppor@ (0000)
002A63D0 003c:
??_C@_0DM@FNPDMPEA@tag_data_resize?$CI?$CJ?5is?5not?5support@ (0000)
002A6410 0042:
??_C@_0EC@MOKCAFPK@tag_block_add_element?$CI?$CJ?5is?5not?5s@ (0000)
002A6458 0045:
??_C@_0EF@NIGPCFB@tag_block_delete_element?$CI?$CJ?5is?5no@ (0000)
002A64A0 0035:
??_C@_0DF@IIJGOBIP@tag_load?$CI?$CJ?5is?5not?5supported?5with@ (0000)
002A64D8 0037:
??_C@_0DH@OJBEKPBJ@tag_unload?$CI?$CJ?5is?5not?5supported?5wi@ (0000)
002A6510 003e:
??_C@_0DO@CMGIBDCM@tag_file_get_path?$CI?$CJ?5is?5not?5suppo@ (0000)
002A6550 003e:
??_C@_0DO@PNBFAACM@tag_reference_set?$CI?$CJ?5is?5not?5suppo@ (0000)
002A6590 0022:
??_C@_0CC@JDEMIPEM@i?5don?8t?5think?5?$CF08x?5is?5a?5tag?5inde@ (0000)
002A65B4 0028:
??_C@_0CI@MCDCHEHF@?8?$CFs?8?5does?5not?5appear?5to?5be?5a?5cac@ (0000)
002A65DC 0036:
??_C@_0DG@MBMNLMG@the?5cache?5file?5?8?$CFs?8?5belongs?5to?5a@ (0000)
002A6614 0026:
??_C@_0CG@GAKJDLAF@the?5cache?5file?5?8?$CFs?8?5is?5an?5old?5ve@ (0000)
002A663C 002e:
??_C@_0CO@BLPPGPI@signature?5is?5?8?$CFc?$CFc?$CFc?$CFc?8?0?5should?5@ (0000)
002A666C 002b:
??_C@_0CL@BCHNHKGI@tag_instance?9?$DOgroup_tag?$DN?$DNSTRUCTU@ (0000)
002A6698 001c:
??_C@_0BM@EFCFDCHK@?$CBtag_instance?9?$DObase_address?$AA@ (0000)
002A66B8 005e:
??_C@_0FO@FEJCGGNA@cache_file_globals?4structure_bsp@ (0000)
002A6718 001b:
??_C@_0BL@OICFEJJN@tag_instance?9?$DObase_address?$AA@ (0000)
002A6734 0027:
??_C@_0CH@HHANGOKG@can?8t?5get?$CI?$CJ?5a?5tag?5with?5a?5base?5ad@ (0000)
002A675C 002e:
??_C@_0CO@IDEKIPEG@expected?5tag?5group?5?8?$CFs?8?5but?5got?5@ (0000)
00316820 0018:
_data_00316820 (0000)
004CCB20 080c:
_bss_004ccb20 (0000)
*/
/* ---------- headers */
#include "cseries.h"
#include "cseries_windows.h"
#include "errors.h"
#include "tag_files/tag_groups.h"
#include "tag_files/files.h"
#include "cache_files.h"
#include "physical_memory_map.h"
#include "sound_cache.h"
#include "texture_cache.h"
#include "interface/ui_widget.h"
#include "scenario/scenario_definitions.h"
#include "sound/sound_manager.h"
/* ---------- constants */
/* ---------- macros */
#define STRUCTURE_BSP_TAG 'sbsp'
#define CACHE_FILE_TAG_HEADER_SIGNATURE 'tags'
#define CACHE_FILE_STRUCTURE_BSP_HEADER_SIGNATURE 'sbsp'
#define CACHE_FILE_HEADER_SIGNATURE 'head'
#define CACHE_FILE_FOOTER_SIGNATURE 'foot'
/* ---------- structures */
struct cache_file_tag_instance
{
long group_tag;
long parent_group_tags[2];
long tag_index;
char *name;
void *base_address;
unsigned long unused[2];
};
struct cache_file_tag_header
{
struct cache_file_tag_instance *tag_instances;
long scenario_tag_index;
unsigned long checksum;
long tag_count;
long vertex_buffer_count;
void *vertex_buffers;
long index_buffer_count;
void *index_buffers;
unsigned long signature;
};
struct cache_file_structure_bsp_header
{
void *base_address;
long vertex_buffer_count;
void *vertex_buffers;
long index_buffer_count;
void *index_buffers;
unsigned long signature;
};
struct cache_file_header
{
unsigned long header_signature;
long version;
long file_length;
byte reservedC[4];
long tag_data_offset;
long tag_data_size;
byte reserved18[8];
char name[0x20];
char build[0x20];
byte reserved60[4];
unsigned long checksum;
byte reserved68[0x794];
unsigned long footer_signature;
};
struct cache_file_globals
{
boolean tags_loaded;
byte pad1[3];
struct cache_file_header header;
struct cache_file_tag_header *tag_header;
struct cache_file_structure_bsp_header *structure_bsp_header;
};
typedef char verify_cache_file_tag_instance_size[
sizeof(struct cache_file_tag_instance) == 0x20 ? 1 : -1];
typedef char verify_cache_file_tag_header_count_offset[
offsetof(struct cache_file_tag_header, tag_count) == 0xC ? 1 : -1];
typedef char verify_cache_file_globals_size[
sizeof(struct cache_file_globals) == 0x80C ? 1 : -1];
typedef char verify_cache_file_header_size[
sizeof(struct cache_file_header) == 0x800 ? 1 : -1];
/* ---------- prototypes */
static struct cache_file_tag_instance *cache_get_tag_instance(
long tag_index);
/* ---------- globals */
static struct cache_file_globals cache_file_globals = { 0 };
extern struct cache_file_tag_instance *global_tag_instances;
static char const *data_00316820[] =
{
"d:\\maps_de\\",
"d:\\maps_fr\\",
"d:\\maps_es\\",
"d:\\maps_it\\",
"d:\\maps\\",
NULL
};
/* ---------- private code */
static struct cache_file_tag_instance *cache_get_tag_instance(
long tag_index)
{
short absolute_index;
struct cache_file_tag_instance *tag_instance;
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
518,
cache_file_globals.tags_loaded);
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
519,
global_tag_instances);
absolute_index = (short)tag_index;
match_vassert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
522,
absolute_index >= 0 && absolute_index < cache_file_globals.tag_header->tag_count,
csprintf(temporary, "i don't think %08x is a tag index", tag_index));
tag_instance = &global_tag_instances[absolute_index];
match_vassert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
526,
!(tag_index & 0xFFFF0000) || tag_instance->tag_index == tag_index,
csprintf(temporary, "i don't think %08x is a tag index", tag_index));
return tag_instance;
}
/* ---------- public code */
char const *cache_files_map_directory(
void)
{
char const *map_directory;
struct file_reference reference;
long directory_index;
switch (XGetLanguage())
{
case XC_LANGUAGE_GERMAN:
map_directory = "d:\\maps_de\\";
break;
case XC_LANGUAGE_FRENCH:
map_directory = "d:\\maps_fr\\";
break;
case XC_LANGUAGE_SPANISH:
map_directory = "d:\\maps_es\\";
break;
case XC_LANGUAGE_ITALIAN:
map_directory = "d:\\maps_it\\";
break;
default:
map_directory = "d:\\maps\\";
break;
}
if (!file_exists(file_reference_create_from_path(&reference, map_directory, TRUE)))
{
for (directory_index = 0; data_00316820[directory_index]; directory_index++)
{
if (file_exists(file_reference_create_from_path(
&reference,
data_00316820[directory_index],
TRUE)))
{
map_directory = data_00316820[directory_index];
break;
}
}
match_vassert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
60,
data_00316820[directory_index],
"no valid map directory exists");
}
return map_directory;
}
void scenario_tags_unload(
void)
{
/* port: the high-res HUD forgets this map's bitmaps (port/linux/game/hud_hires_tags.c) */
{
extern void hud_hires_tags_unloaded(void);
hud_hires_tags_unloaded();
}
sound_cache_close();
texture_cache_close();
cache_file_close();
tags_header_deregister_vertex_and_index_buffers(cache_file_globals.tag_header);
cache_file_globals.tags_loaded = FALSE;
global_tag_instances = NULL;
return;
}
void tag_files_open(
void)
{
cache_files_initialize();
return;
}
void tag_files_close(
void)
{
cache_files_dispose();
return;
}
unsigned long cache_files_get_checksum(
void)
{
return cache_file_globals.header.checksum;
}
unsigned long tag_groups_checksum(
void)
{
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
327,
cache_file_globals.tags_loaded);
return cache_file_globals.tag_header->checksum;
}
long tag_loaded(
long group_tag,
char const *name)
{
short absolute_index;
long result = NONE;
if (cache_file_globals.tags_loaded)
{
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
346,
global_tag_instances);
for (absolute_index = 0;
absolute_index < cache_file_globals.tag_header->tag_count;
absolute_index++)
{
if (group_tag == global_tag_instances[absolute_index].group_tag &&
!_stricmp(name, global_tag_instances[absolute_index].name))
{
result = global_tag_instances[absolute_index].tag_index;
break;
}
}
}
return result;
}
void cache_files_enable_writes(
void)
{
XPhysicalProtect((void *)0x803A6000, 0x01600000, PAGE_READWRITE);
return;
}
void cache_files_disable_writes(
void)
{
XPhysicalProtect((void *)0x803A6000, 0x01600000, PAGE_READONLY);
XPhysicalProtect(
cache_file_globals.tag_header->vertex_buffers,
cache_file_globals.tag_header->vertex_buffer_count * 12,
PAGE_READWRITE);
XPhysicalProtect(
cache_file_globals.tag_header->index_buffers,
cache_file_globals.tag_header->index_buffer_count * 12,
PAGE_READWRITE);
if (cache_file_globals.structure_bsp_header)
{
XPhysicalProtect(
cache_file_globals.structure_bsp_header->vertex_buffers,
cache_file_globals.structure_bsp_header->vertex_buffer_count * 12,
PAGE_READWRITE);
XPhysicalProtect(
cache_file_globals.structure_bsp_header->index_buffers,
cache_file_globals.structure_bsp_header->index_buffer_count * 12,
PAGE_READWRITE);
}
return;
}
boolean tag_block_resize(
struct tag_block *block,
long count)
{
error(_error_silent, "tag_block_resize() is not supported with a cache file active");
return FALSE;
}
boolean tag_data_resize(
struct tag_data *data,
long size)
{
error(_error_silent, "tag_data_resize() is not supported with a cache file active");
return FALSE;
}
long tag_block_add_element(
struct tag_block *block)
{
error(_error_silent, "tag_block_add_element() is not supported with a cache file active");
return NONE;
}
void tag_block_delete_element(
struct tag_block *block,
long element_index)
{
error(_error_silent, "tag_block_delete_element() is not supported with a cache file active");
return;
}
long tag_load(
long group_tag,
char const *name,
unsigned long flags)
{
error(_error_silent, "tag_load() is not supported with a cache file active");
return NONE;
}
void tag_unload(
long tag_index)
{
error(_error_silent, "tag_unload() is not supported with a cache file active");
return;
}
void tag_file_get_path(
long group_tag,
char const *name,
char *path)
{
error(_error_silent, "tag_file_get_path() is not supported with a cache file active");
path[0] = 0;
return;
}
void tag_reference_set(
struct tag_reference *reference,
unsigned long group_tag,
char const *name)
{
error(_error_silent, "tag_reference_set() is not supported with a cache file active");
return;
}
void tag_iterator_new(
struct tag_iterator *iterator,
long group_tag)
{
iterator->absolute_index = 0;
iterator->group_tag = group_tag;
return;
}
long tag_iterator_next(
struct tag_iterator *iterator)
{
long result = NONE;
while (iterator->absolute_index < cache_file_globals.tag_header->tag_count)
{
struct cache_file_tag_instance *tag_instance =
&global_tag_instances[iterator->absolute_index++];
if (tag_instance &&
(iterator->group_tag == NONE ||
iterator->group_tag == tag_instance->group_tag ||
iterator->group_tag == tag_instance->parent_group_tags[0] ||
iterator->group_tag == tag_instance->parent_group_tags[1]))
{
result = tag_instance->tag_index;
break;
}
}
return result;
}
boolean cache_file_header_verify(
struct cache_file_header *header,
char const *scenario_name,
boolean fatal)
{
if (header->header_signature != CACHE_FILE_HEADER_SIGNATURE ||
header->footer_signature != CACHE_FILE_FOOTER_SIGNATURE ||
header->file_length < 0 ||
header->file_length > 0x11600000 ||
csstrlen(header->name) > 31)
{
if (fatal)
{
match_vassert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
544,
FALSE,
csprintf(temporary, "'%s' does not appear to be a cache file", scenario_name));
}
return FALSE;
}
if (header->version != 5)
{
if (fatal)
{
match_vassert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
548,
FALSE,
csprintf(temporary, "the cache file '%s' is an old version", scenario_name));
}
return FALSE;
}
return TRUE;
}
/* port: the builds of the released maps, by region. Any build here plays
multiplayer with the others; a map of another build may differ in what
machines send each other, so its players cannot open the multiplayer menu
(ui_widget.c, ui_widget_launch_widget). A PAL build's maps are played as the
NTSC maps are (port/linux/game/pal_tags.c) */
static struct
{
char const *build;
char const *region;
} const cache_file_builds[] =
{
{ "01.01.14.2342", "PAL" },
{ "01.10.12.2276", "NTSC" },
{ "01.08.15.1749", "NTSC" },
};
/* the region of a build's maps ("PAL" or "NTSC") if it is listed above, else
NULL; build is a cache file header's (which need not end it) */
char const *cache_files_build_region(
char const *build)
{
short index;
for (index = 0; index < NUMBEROF(cache_file_builds); index++)
{
if (!csstrncmp(build, cache_file_builds[index].build, sizeof(cache_file_globals.header.build)))
return cache_file_builds[index].region;
}
return NULL;
}
/* the region of the loaded map's build if it plays multiplayer, else NULL;
build gets the build */
char const *cache_files_multiplayer_region(
char build[0x20])
{
csstrncpy(build, cache_file_globals.header.build, 0x20);
build[0x1F] = 0;
return cache_files_build_region(cache_file_globals.header.build);
}
/* whether the named map plays multiplayer with the others: FALSE only for
a map whose header is of a build not listed above (a map whose header cannot
be read is left to precaching, which tells of a missing map); build gets the
map's build, empty if unread. The multiplayer menus check the loaded map's
(ui.map's) build; this checks a multiplayer map's own, which may be of
another build: the object and damage messages name definitions by tag
index, which differs between builds */
boolean cache_files_map_plays_multiplayer(
char const *map_name,
char build[0x20])
{
struct cache_file_header header;
char path[256];
HANDLE file;
boolean result = TRUE;
build[0] = 0;
if (!map_name || !map_name[0])
return TRUE;
snprintf(path, sizeof(path), "%s%s.map", cache_files_map_directory(), tag_name_strip_path(map_name));
file = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (file != INVALID_HANDLE_VALUE)
{
unsigned long bytes_read;
if (ReadFile(file, &header, sizeof(header), &bytes_read, NULL) &&
bytes_read == sizeof(header) &&
cache_file_header_verify(&header, path, FALSE))
{
csstrncpy(build, header.build, 0x20);
build[0x1F] = 0;
result = cache_files_build_region(header.build) != NULL;
}
CloseHandle(file);
}
return result;
}
/* tells the player that maps of a build (a cache file header's) do not play
multiplayer: map_name the map's, or NULL for the player's maps */
void cache_files_show_multiplayer_unavailable(
char const *map_name,
char const *build)
{
void platform_log(char const *format, ...);
void platform_show_message(char const *title, char const *message);
char message[320];
if (map_name)
{
platform_log("multiplayer is unavailable: the map %s is of build %s, which is not supported", map_name, build);
snprintf(
message,
sizeof(message),
"The map %s (build %s) isn't supported for multiplayer yet.\n\nAsk in the Discord to get it added.",
tag_name_strip_path(map_name),
build);
}
else
{
platform_log("multiplayer is unavailable: maps of build %s are not supported", build);
snprintf(
message,
sizeof(message),
"Your maps (build %s) aren't supported for multiplayer yet.\n\nAsk in the Discord to get them added.",
build);
}
platform_show_message("Halo: multiplayer unavailable", message);
return;
}
boolean cache_files_give_time_to_precache(
char const *map_name)
{
boolean result = FALSE;
/* port: no map named yet is nothing to precache. A client joining over
the internet asks for its multiplayer map (network_game_client_update_precache_status)
before the host's settings name it: an empty name, which matched a cache
file slot not yet used, and once all six hold maps (two campaign levels,
the main menu and three multiplayer maps played) matched none, so was
taken for a map missing from the disc (the damaged disc error) */
if (!map_name || !map_name[0])
return FALSE;
if (cache_files_precache_map_loaded(map_name))
{
result = TRUE;
}
else
{
if (cache_files_precache_in_progress() &&
!cache_files_precache_is_copying_map(map_name))
{
cache_files_precache_map_end();
}
if (cache_files_precache_in_progress())
{
real progress;
short status = cache_files_precache_map_status(&progress);
if (status == 2)
display_error_damaged_media();
else if (status == 1)
cache_files_precache_map_end();
}
else
{
cache_files_precache_set_priority(0);
if (!cache_files_precache_map_begin(map_name, FALSE))
display_error_damaged_media();
}
}
return result;
}
long scenario_tags_load(
char const *scenario_name)
{
long result;
char const *stripped_scenario_name;
void *tag_cache_base_address;
boolean read_complete;
stripped_scenario_name = tag_name_strip_path(scenario_name);
result = NONE;
texture_cache_open();
sound_cache_open();
if (cache_file_open(stripped_scenario_name, &cache_file_globals.header))
{
tag_cache_base_address = physical_memory_get_tag_cache_base_address();
if (cache_file_header_verify(&cache_file_globals.header, scenario_name, TRUE))
{
csmemset(tag_cache_base_address, 0xCD, 0x01600000);
cache_file_read(
NONE,
cache_file_globals.header.tag_data_offset,
cache_file_globals.header.tag_data_size,
tag_cache_base_address,
&read_complete,
TRUE);
while (!read_complete)
{
SwitchToThread();
}
cache_file_globals.tag_header = tag_cache_base_address;
match_vassert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
0x94,
cache_file_globals.tag_header->signature == CACHE_FILE_TAG_HEADER_SIGNATURE,
csprintf(
temporary,
"signature is '%c%c%c%c', should be '%c%c%c%c'",
((char *)&cache_file_globals.tag_header->signature)[3],
((char *)&cache_file_globals.tag_header->signature)[2],
((char *)&cache_file_globals.tag_header->signature)[1],
((char *)&cache_file_globals.tag_header->signature)[0],
't',
'a',
'g',
's'));
global_tag_instances = cache_file_globals.tag_header->tag_instances;
tags_header_register_vertex_and_index_buffers(cache_file_globals.tag_header);
cache_file_globals.tags_loaded = TRUE;
/* port: a PAL map played as the NTSC maps are (port/linux/game/pal_tags.c) */
{
extern void pal_tags_loaded(char const *build);
pal_tags_loaded(cache_file_globals.header.build);
}
/* port: the bitmaps the high-res HUD stands for (port/linux/game/hud_hires_tags.c) */
{
extern void hud_hires_tags_loaded(void);
hud_hires_tags_loaded();
}
#ifdef HALO_VR
/* port: the pause menu's VR settings (port/linux/game/vr_menu.c) */
{
extern void vr_menu_tags_loaded(void);
vr_menu_tags_loaded();
}
#endif
result = cache_file_globals.tag_header->scenario_tag_index;
}
}
return result;
}
boolean scenario_structure_bsp_load(
struct scenario_structure_bsp_reference *reference)
{
struct cache_file_tag_instance *tag_instance;
byte *tag_cache_base_address;
tag_cache_base_address = physical_memory_get_tag_cache_base_address();
csmemset(
tag_cache_base_address + cache_file_globals.header.tag_data_size,
0xCD,
0x01600000 - cache_file_globals.header.tag_data_size);
{
boolean read_complete;
cache_file_read(
NONE,
reference->file_offset,
reference->file_size,
reference->base_address,
&read_complete,
TRUE);
while (!read_complete)
{
SwitchToThread();
if (system_milliseconds() - sound_render_time() > 33)
{
sound_idle();
}
}
}
cache_file_globals.structure_bsp_header = reference->base_address;
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
0xE0,
cache_file_globals.structure_bsp_header->signature==CACHE_FILE_STRUCTURE_BSP_HEADER_SIGNATURE);
structure_bsp_header_register_vertex_buffers(cache_file_globals.structure_bsp_header);
tag_instance = cache_get_tag_instance(reference->structure_bsp.index);
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
0xEA,
!tag_instance->base_address);
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
0xEB,
tag_instance->group_tag==STRUCTURE_BSP_TAG);
tag_instance->base_address = cache_file_globals.structure_bsp_header->base_address;
return TRUE;
}
void scenario_structure_bsp_unload(
struct scenario_structure_bsp_reference *reference)
{
struct cache_file_tag_instance *tag_instance;
structure_bsp_header_deregister_vertex_buffers(cache_file_globals.structure_bsp_header);
tag_instance = cache_get_tag_instance(reference->structure_bsp.index);
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
256,
tag_instance->base_address);
match_assert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
257,
tag_instance->group_tag==STRUCTURE_BSP_TAG);
tag_instance->base_address = NULL;
cache_file_globals.structure_bsp_header = NULL;
return;
}
/* port: a tag made in memory while a map is loaded (its definition at
`base_address`, which the caller keeps until the next map's tags load),
added after the map's own: its tag index, or NONE. The map's table of tags
is copied the first time, with room for more. */
long cache_file_add_tag(
unsigned long group_tag,
unsigned long parent_group_tag,
char *name,
void *base_address)
{
enum { ADDED_TAG_ROOM = 64, ADDED_TAG_SALT = 0x5652 };
static struct cache_file_tag_instance *instances = NULL;
static long capacity = 0;
struct cache_file_tag_instance *instance;
long count;
if (!cache_file_globals.tags_loaded || !global_tag_instances)
return NONE;
count = cache_file_globals.tag_header->tag_count;
if (global_tag_instances != instances)
{
/* (the last map's copy goes with it; the game's free halts on NULL) */
if (instances)
free(instances);
capacity = count + ADDED_TAG_ROOM;
instances = malloc(capacity * sizeof(*instances));
if (!instances)
{
capacity = 0;
return NONE;
}
memcpy(instances, global_tag_instances, count * sizeof(*instances));
global_tag_instances = instances;
}
if (count >= capacity || count >= 0x7FFF)
return NONE;
instance = &instances[count];
memset(instance, 0, sizeof(*instance));
instance->group_tag = (long)group_tag;
instance->parent_group_tags[0] = (long)parent_group_tag;
instance->parent_group_tags[1] = NONE;
instance->tag_index = (long)((ADDED_TAG_SALT << 16) | count);
instance->name = name;
instance->base_address = base_address;
cache_file_globals.tag_header->tag_count = count + 1;
return instance->tag_index;
}
void *tag_get(
long group_tag,
long tag_index)
{
char expected_group[16];
char returned_group[16];
struct cache_file_tag_instance *tag_instance = cache_get_tag_instance(tag_index);
match_vassert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
298,
tag_instance->group_tag == group_tag ||
tag_instance->parent_group_tags[0] == group_tag ||
tag_instance->parent_group_tags[1] == group_tag,
csprintf(
temporary,
"expected tag group '%s' but got '%s' for %08x",
tag_to_string(group_tag, expected_group),
tag_to_string(tag_instance->group_tag, returned_group),
tag_index)
);
match_vassert(
"c:\\halo\\SOURCE\\cache\\cache_files.c",
302,
tag_instance->base_address,
csprintf(temporary, "can't get() a tag with a base address!")
);
return tag_instance->base_address;
}
/* whether the index is a loaded tag of the group (or a group it inherits
from): the distributed netcode names tags another machine sent
(port/linux/game/network_damage.c), which tag_get would only assert on */
boolean tag_index_is_group(
long tag_index,
long group_tag)
{
short absolute_index = (short)tag_index;
struct cache_file_tag_instance *tag_instance;
if (tag_index == NONE || !cache_file_globals.tags_loaded || !global_tag_instances ||
absolute_index < 0 || absolute_index >= cache_file_globals.tag_header->tag_count)
{
return FALSE;
}
tag_instance = &global_tag_instances[absolute_index];
return tag_instance->tag_index == tag_index && tag_instance->base_address &&
(tag_instance->group_tag == group_tag || tag_instance->parent_group_tags[0] == group_tag ||
tag_instance->parent_group_tags[1] == group_tag);
}
char *tag_get_name(
long tag_index)
{
return cache_get_tag_instance(tag_index)->name;
}
unsigned long tag_get_group_tag(
long tag_index)
{
return cache_get_tag_instance(tag_index)->group_tag;
}