/* DATA.C symbols in this file: 001089A0 0020: _data_allocation_size (0000) 001089C0 00d0: _data_initialize (0000) 00108A90 00b0: _datum_try_and_get (0000) 00108B40 00d0: _datum_get (0000) 00108C10 00b0: _data_verify (0000) 00108CC0 0030: _datum_initialize (0000) 00108CF0 0050: _data_new (0000) 00108D40 0030: _data_dispose (0000) 00108D70 0020: _data_make_invalid (0000) 00108D90 00a0: _datum_new_at_index (0000) 00108E30 00c0: _datum_new (0000) 00108EF0 0050: _datum_delete (0000) 00108F40 0090: _data_delete_all (0000) 00108FD0 0060: _data_iterator_new (0000) 00109030 00e0: _data_iterator_next (0000) 00109110 0090: _data_next_index (0000) 001091A0 0090: _data_prev_index (0000) 00109230 0110: _data_compact (0000) 00109340 0020: _data_make_valid (0000) 0027D310 0005: ??_C@_04MEMAJGDJ@name?$AA@ (0000) 0027D318 0010: ??_C@_0BA@HDDJANCC@maximum_count?$DO0?$AA@ (0000) 0027D328 001d: ??_C@_0BN@PKENIDAC@c?3?2halo?2SOURCE?2memory?2data?4c?$AA@ (0000) 0027D348 002d: ??_C@_0CN@EMICPANB@identifier?5?$HM?$HM?5?$CBdata?9?$DOidentifier_@ (0000) 0027D378 000c: ??_C@_0M@FOPPJIAE@data?9?$DOvalid?$AA@ (0000) 0027D384 0029: ??_C@_0CJ@PGGNIIAJ@?$CFs?5index?5?$CD?$CFd?5?$CI0x?$CFx?$CJ?5is?5unused?5or@ (0000) 0027D3B0 002a: ??_C@_0CK@PGJBLDEL@?$CFs?5data?5array?5?$EA?$CFp?5is?5bad?5or?5not?5@ (0000) 0027D3DC 0016: ??_C@_0BG@NCCMBKGF@iterator?9?$DOdata?9?$DOvalid?$AA@ (0000) 0027D3F4 0031: ??_C@_0DB@COCANJJJ@uninitialized?5iterator?5passed?5to@ (0000) */ /* ---------- headers */ #include "cseries.h" #include "data.h" /* ---------- constants */ /* ---------- macros */ /* ---------- structures */ /* ---------- prototypes */ static void datum_initialize(struct data_array *data, struct datum_header *header); static boolean data_usable(struct data_array *data, void const *caller); void platform_log(const char *format, ...); /* ---------- globals */ /* ---------- public code */ long data_allocation_size( short maximum_count, short size) { return maximum_count*size+sizeof(struct data_array); } void data_initialize( struct data_array *data, const char *name, short maximum_count, short size) { match_assert("c:\\halo\\SOURCE\\memory\\data.c", 64, maximum_count>0); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 65, size>0); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 66, name); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 67, data); csmemset(data, 0, sizeof(*data)); csstrncpy(data->name, name, sizeof(data->name)-1); data->maximum_count = maximum_count; data->size = size; data->signature = 'd@t@'; data->data = data+1; data->valid = FALSE; return; } void *datum_try_and_get( struct data_array *data, long index) { struct datum_header *header = NULL; short identifier; short absolute_index; if (index!=NONE) { identifier = index>>16; absolute_index = index; match_assert("c:\\halo\\SOURCE\\memory\\data.c", 371, data->valid); match_assert( "c:\\halo\\SOURCE\\memory\\data.c", 372, identifier || !data->identifier_zero_invalid); if (absolute_index>=0 && absolute_indexmaximum_count) { header = (struct datum_header *)((byte *)data->data+data->size*absolute_index); if (!header->identifier || (identifier && header->identifier!=identifier)) { header = NULL; } } } return header; } void *datum_get( struct data_array *data, long index) { struct datum_header *header; short identifier; short absolute_index; identifier = index>>16; absolute_index = index; match_assert("c:\\halo\\SOURCE\\memory\\data.c", 396, data->valid); match_assert( "c:\\halo\\SOURCE\\memory\\data.c", 397, identifier || !data->identifier_zero_invalid); if (absolute_index>=0 && absolute_indexcount) { header = (struct datum_header *)((byte *)data->data+data->size*absolute_index); if (header->identifier && (!identifier || identifier==header->identifier)) { return header; } } #ifdef HALO_RELEASE /* port: a release build's message too, and where it was asked from (debug.txt's reference address places it in the build): the caller goes on with the NULL and crashes, with nothing else to say which array and which caller it was */ { char message[128]; csprintf( message, "%.32s index #%d (0x%x) is unused or changed (asked from %p)", data->name, index&0xFFFF, index, __builtin_return_address(0)); release_assert_failed(message, "c:\\halo\\SOURCE\\memory\\data.c", 412, TRUE); } #else match_vassert( "c:\\halo\\SOURCE\\memory\\data.c", 412, FALSE, csprintf( temporary, "%s index #%d (0x%x) is unused or changed", data->name, index&0xFFFF, index)); #endif return NULL; } void data_verify( struct data_array *data) { match_assert("c:\\halo\\SOURCE\\memory\\data.c", 457, data); match_vassert( "c:\\halo\\SOURCE\\memory\\data.c", 470, data->data && data->signature=='d@t@' && data->maximum_count>=0 && data->count>=0 && data->count<=data->maximum_count && data->first_free_absolute_index>=0 && data->first_free_absolute_index<=data->maximum_count && data->actual_count>=0 && data->actual_count<=data->count, csprintf(temporary, "%s data array @%p is bad or not allocated", data->name, data)); return; } struct data_array *data_new( const char *name, short maximum_count, short size) { struct data_array *data = (struct data_array *)match_malloc( "c:\\halo\\SOURCE\\memory\\data.c", 41, data_allocation_size(maximum_count, size)); if (data) { data_initialize(data, name, maximum_count, size); } return data; } void data_dispose( struct data_array *data) { data_verify(data); csmemset(data, 0, sizeof(*data)); match_free("c:\\halo\\SOURCE\\memory\\data.c", 89, data); return; } void data_make_invalid( struct data_array *data) { data_verify(data); data->valid = FALSE; return; } long datum_new_at_index( struct data_array *data, long index) { struct datum_header *header; short absolute_index = index; short identifier = index>>16; long result = NONE; data_verify(data); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 123, data->valid); if (!data_usable(data, __builtin_return_address(0))) return NONE; if (absolute_index>=0 && absolute_indexmaximum_count && identifier) { header = (struct datum_header *)((byte *)data->data+data->size*absolute_index); if (!header->identifier) { data->actual_count++; if (absolute_index>=data->count) { data->count = absolute_index+1; } datum_initialize(data, header); header->identifier = identifier; result = identifier<<16 | absolute_index; } } return result; } long datum_new( struct data_array *data) { struct datum_header *header; short absolute_index; long size; long result = NONE; data_verify(data); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 163, data->valid); if (!data_usable(data, __builtin_return_address(0))) return NONE; absolute_index = data->first_free_absolute_index; size = data->size; header = (struct datum_header *)((byte *)data->data+size*absolute_index); while (absolute_indexmaximum_count) { if (!header->identifier) { datum_initialize(data, header); data->actual_count++; data->first_free_absolute_index = absolute_index+1; if (data->count<=absolute_index) { data->count = absolute_index+1; } result = header->identifier<<16 | absolute_index; break; } absolute_index++; header = (struct datum_header *)((byte *)header+size); } /* port: none free from first_free_absolute_index on (and none before it is, as ever): the next search starts at the end, until a datum is deleted (datum_delete lowers it), rather than walking a full array again. A full array (the AI's props, with many actors) was walked on every new datum. */ if (result == NONE) data->first_free_absolute_index = data->maximum_count; return result; } void data_make_valid( struct data_array *data) { data_verify(data); data->valid = TRUE; data_delete_all(data); return; } void datum_delete( struct data_array *data, long index) { struct datum_header *header = (struct datum_header *)datum_get(data, index); header->identifier = 0; if (((short)index)first_free_absolute_index) { data->first_free_absolute_index = index; } if (((short)index)+1==data->count) { do { header =(struct datum_header *)((byte *)header-data->size); data->count--; } while (data->count > 0 && !header->identifier); } data->actual_count--; return; } void data_delete_all( struct data_array *data) { short absolute_index; data_verify(data); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 226, data->valid); data->count = 0; data->actual_count = 0; data->first_free_absolute_index = 0; csstrncpy((char *)&data->next_identifier, data->name, sizeof(data->next_identifier)); data->next_identifier |= 0x8000; for (absolute_index = 0; absolute_indexmaximum_count; absolute_index++) { ((struct datum_header *)((byte *)data->data+data->size*absolute_index))->identifier = 0; } return; } void data_iterator_new( struct data_iterator *iterator, struct data_array *data) { data_verify(data); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 249, data->valid); iterator->data = data; iterator->signature = (unsigned long)data^'iter'; iterator->absolute_index = 0; iterator->datum_index = NONE; return; } void *data_iterator_next( struct data_iterator *iterator) { struct datum_header *header; short absolute_index; long datum_index; long size; void *result = NULL; match_vassert( "c:\\halo\\SOURCE\\memory\\data.c", 268, iterator->signature==((unsigned long)iterator->data^'iter'), "uninitialized iterator passed to iterator_next()"); data_verify(iterator->data); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 271, iterator->data->valid); absolute_index = iterator->absolute_index; size = iterator->data->size; header = (struct datum_header *)((byte *)iterator->data->data+size*absolute_index); while (absolute_indexdata->count) { datum_index = header->identifier<<16 | absolute_index; absolute_index++; if (header->identifier) { iterator->datum_index = datum_index; result = header; break; } header = (struct datum_header *)((byte *)header+size); } iterator->absolute_index = absolute_index; return result; } long data_next_index( struct data_array *data, long index) { struct datum_header *header; long result = NONE; short absolute_index = index+1; data_verify(data); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 303, data->valid); if (absolute_index>=0 && absolute_indexcount) { header = (struct datum_header *)((byte *)data->data+data->size*absolute_index); while (absolute_indexcount) { if (header->identifier) { result = header->identifier<<16 | absolute_index; break; } absolute_index++; header = (struct datum_header *)((byte *)header+data->size); } } return result; } long data_prev_index( struct data_array *data, long index) { struct datum_header *header; long result = NONE; short absolute_index; data_verify(data); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 335, data->valid); absolute_index = index==NONE ? data->count-1 : index-1; if (absolute_index>=0 && absolute_indexcount) { header = (struct datum_header *)((byte *)data->data+data->size*absolute_index); do { if (header->identifier) { result = header->identifier<<16 | absolute_index; break; } header = (struct datum_header *)((byte *)header-data->size); } while (absolute_index-->=0); } return result; } void data_compact( struct data_array *data) { void *compacted_data; /* Keep the null sentinel distinct; VC7 coalesces it with compacted_count. */ void *empty = NULL; struct datum_header *datum; short compacted_count; short absolute_index; compacted_data = match_malloc( "c:\\halo\\SOURCE\\memory\\data.c", 421, data->maximum_count*data->size); data_verify(data); match_assert("c:\\halo\\SOURCE\\memory\\data.c", 424, data->valid); if (compacted_data!=empty) { datum = data->data; absolute_index = (long)empty; compacted_count = (short)(long)empty; while (absolute_indexcount) { if (datum->identifier) { csmemcpy( (byte *)compacted_data+compacted_count*data->size, datum, data->size); compacted_count++; } absolute_index++; datum = (struct datum_header *)((byte *)datum+data->size); } csmemcpy(data->data, compacted_data, compacted_count*data->size); csmemset( (byte *)data->data+compacted_count*data->size, 0, (data->maximum_count-compacted_count)*data->size); data->actual_count = compacted_count; data->count = compacted_count; data->first_free_absolute_index = compacted_count; match_free("c:\\halo\\SOURCE\\memory\\data.c", 447, compacted_data); } return; } /* ---------- private code */ /* port: whether an array gives out datums: one that is an array (its signature, its elements) made valid for the map (data_make_valid). The release game went on past the asserts, and an array never made valid, with no identifier yet (data_delete_all seeds it), gave out identifier 0, which marks a free datum: datum_get of it returned NULL to a caller that wrote through it (light_new: Sentry NATIVE-7 and NATIVE-9). Each array is reported once, to halo.log, with the first caller turned away. */ static boolean data_usable( struct data_array *data, void const *caller) { static struct data_array const *reported[32]; static long reported_count; long index; if (data->signature == 'd@t@' && data->data && data->valid && data->next_identifier) return TRUE; for (index = 0; index < reported_count; index++) { if (reported[index] == data) return FALSE; } if (reported_count < (long)NUMBEROF(reported)) reported[reported_count++] = data; platform_log("data: %.32s gives out no datum: signature %08lx, %s, next identifier %04x, count %d of %d " "(asked from %p)", data->signature == 'd@t@' ? data->name : "(not an array)", (unsigned long)data->signature, data->valid ? "valid" : "not valid", (unsigned short)data->next_identifier, data->count, data->maximum_count, caller); return FALSE; } static void datum_initialize( struct data_array *data, struct datum_header *header) { csmemset(header, 0, data->size); header->identifier = data->next_identifier; data->next_identifier++; if (!data->next_identifier) { data->next_identifier = 0x8000; } return; }