Files
Cesarus85--ringfall-mjolnir-vr/source/bitmaps/bitmap_extract.c
T
Jonas Volman f3aadeab98 Reconcile five Halo functions and approved declaration batches
Integrate coherent B3, P2/K17 prerequisites, completed K1-K16 + S, and the independent profile/effects rulings. Five gains and two explicitly approved B3 debits: net +3 strict functions, +2770 meaningful/+2800 padded bytes (+2774 progress credit). No additional losses; Halo objects 394 to 396 and data unchanged.

Clean rebuild, identical 8252-row stable sweep, 68 valid parks, admission/provider checks, 467-unit warning comparison, unchanged fake scan, and 1318 tests passed. Preserve canonical helper recoveries and unrelated work. Full evidence and disclosures in docs/object_matching_logs/astra_five_function_reconciliation_20260928.md.
2026-09-28 23:17:10 -07:00

2741 lines
71 KiB
C

/*
BITMAP_EXTRACT.C
symbols in this file:
00061EA0 0130:
_extract_initialize (0000)
00061FD0 0120:
_extract_find_sequence_bounds (0000)
000620F0 0080:
_extract_warn_about_horizontal_border (0000)
00062170 0340:
_extract_plateless_cube_map (0000)
000624B0 0190:
_extract_find_bitmap_bounds (0000)
00062640 0240:
_extract_get_bitmap_format (0000)
00062880 03b0:
_extract_pixels_to_mipmap (0000)
00062C30 0210:
_extract_pixels_from_mipmap (0000)
00062E40 0260:
_extract_build_texture_pages_by_sequence (0000)
000630A0 0580:
_extract_build_debug_plate (0000)
00063620 03d0:
_extract_mipmaps_to_bitmap (0000)
000639F0 02b0:
_extract_add_bitmap (0000)
00063CA0 01d0:
_extract_3d_textures (0000)
00063E70 0220:
_extract_cube_maps (0000)
00064090 0450:
_extract_sprites (0000)
000644E0 0490:
_extract_bitmap (0000)
00064970 0110:
_extract_without_sequences (0000)
00064A80 02d0:
_extract_sequence (0000)
00064D50 00b0:
_extract_sequences (0000)
00064E00 02e0:
_bitmaps_extract (0000)
000650E0 0130:
_bitmaps_extract_from_plate (0000)
00251290 000e:
??_C@_0O@IOJAHIHH@top_reference?$AA@ (0000)
002512A0 0028:
??_C@_0CI@MDNPJHJC@c?3?2halo?2SOURCE?2bitmaps?2bitmap_ex@ (0000)
002512C8 0034:
??_C@_0DE@MPBEDKDP@?$CD?$CD?$CD?5WARNING?5horizontal?5border?5br@ (0000)
00251300 0045:
??_C@_0EF@HOBDLMPH@?$CD?$CD?$CD?5ERROR?5extract?3?5plateless?5cub@ (0000)
00251348 0042:
??_C@_0EC@CNAGFICN@?$CD?$CD?$CD?5ERROR?5extract?3?5can?8t?5handle?5@ (0000)
0025138C 0037:
??_C@_0DH@JLCLDNBC@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5all@ (0000)
002513C4 001c:
??_C@_0BM@HDLJMFAA@bitmap_verify?$CIbitmap?0?5TRUE?$CJ?$AA@ (0000)
002513E0 001a:
??_C@_0BK@OPAPKIME@adjusted_bounds_reference?$AA@ (0000)
002513FC 0007:
??_C@_06CGBPGGIE@bounds?$AA@ (0000)
00251404 0033:
??_C@_0DD@OAKGMMJC@?$CD?$CD?$CD?5ERROR?5extract?3?5unsupported?5b@ (0000)
00251438 0024:
??_C@_0CE@MAGCMLBE@?$CD?$CD?$CD?5ERROR?5unsupported?5bitmap?5for@ (0000)
0025145C 003c:
??_C@_0DM@JCMHMNJE@?$CBTEST_FLAG?$CIdestination_bitmap?9?$DOf@ (0000)
00251498 005a:
??_C@_0FK@KAGBNKPE@destination_mipmap_index?$DO?$DN0?5?$CG?$CG?5d@ (0000)
002514F4 002e:
??_C@_0CO@MKAFAHJI@destination_bitmap?9?$DOtype?$DN?$DNsource@ (0000)
00251524 0029:
??_C@_0CJ@PMPPCDAH@bitmap_verify?$CIdestination_bitmap@ (0000)
00251550 0054:
??_C@_0FE@NNBMCEJD@source_bitmap?9?$DOdepth?5?$DN?$DNMAX?$CI1?0?5de@ (0000)
002515A8 0054:
??_C@_0FE@NIAAMOKD@source_bitmap?9?$DOheight?$DN?$DNMAX?$CI1?0?5de@ (0000)
00251600 0054:
??_C@_0FE@OGKNFLAG@source_bitmap?9?$DOwidth?5?$DN?$DNMAX?$CI1?0?5de@ (0000)
00251654 0023:
??_C@_0CD@ECDENEFB@bitmap_verify?$CIsource_bitmap?0?5TRU@ (0000)
00251678 004b:
??_C@_0EL@CICFBLGI@source_mipmap_index?$DO?$DN0?5?$CG?$CG?5source@ (0000)
002516C4 002e:
??_C@_0CO@LIKJMDNH@source_bitmap?9?$DOtype?$DN?$DNdestination@ (0000)
002516F4 0024:
??_C@_0CE@HECEPIHL@bitmap_verify?$CIsource_bitmap?0?5FAL@ (0000)
00251718 004f:
??_C@_0EP@JCFKDNGM@destination_bitmap?9?$DOdepth?5?$DN?$DNMAX?$CI@ (0000)
00251768 004f:
??_C@_0EP@KOPMFNGM@destination_bitmap?9?$DOheight?$DN?$DNMAX?$CI@ (0000)
002517B8 004f:
??_C@_0EP@OEMHGOAI@destination_bitmap?9?$DOwidth?5?$DN?$DNMAX?$CI@ (0000)
00251808 0028:
??_C@_0CI@JBLFEOIJ@bitmap_verify?$CIdestination_bitmap@ (0000)
00251830 0024:
??_C@_0CE@LCCMCEAJ@sequence?5spanned?5?$CFd?5texture?5page@ (0000)
00251854 0030:
??_C@_0DA@BOIOHNMC@?$CD?$CD?$CD?5ERROR?5failed?5to?5allocate?5deb@ (0000)
00251884 0030:
??_C@_0DA@NMPFGKNI@?$CD?$CD?$CD?5ERROR?5failed?5to?5allocate?5deb@ (0000)
002518B4 002a:
??_C@_0CK@DMHGHFA@?$CD?$CD?$CD?5ERROR?5failed?5to?5allocate?5deb@ (0000)
002518E0 0022:
??_C@_0CC@DECIGGNF@?$CD?$CD?$CD?5ERROR?5unsupported?5bitmap?5typ@ (0000)
00251904 0024:
??_C@_0CE@BAFBIHF@alpha_bias?$DO?$DN?9255?5?$CG?$CG?5alpha_bias?$DM?$DN@ (0000)
00251928 0030:
??_C@_0DA@KOPAIHGJ@destination_bitmap?9?$DOdepth?$DN?$DNsourc@ (0000)
00251958 0032:
??_C@_0DC@IECBCEHB@destination_bitmap?9?$DOheight?$DN?$DNsour@ (0000)
0025198C 0030:
??_C@_0DA@LLNHMAL@destination_bitmap?9?$DOwidth?$DN?$DNsourc@ (0000)
002519BC 0021:
??_C@_0CB@CLPJEELI@destination_bitmap?9?$DObase_address@ (0000)
002519E0 0013:
??_C@_0BD@JMEMKNJH@destination_bitmap?$AA@ (0000)
002519F4 0029:
??_C@_0CJ@PEGGHKHE@bitmap?5created?3?5?$CD?$CFdx?$CD?$CFd?0?5?$CFs?0?5?$CFdK@ (0000)
00251A20 002d:
??_C@_0CN@MGDDLHDM@bitmap?5created?3?5?$CD?$CFdx?$CD?$CFdx?$CD?$CFd?0?5?$CFs?0@ (0000)
00251A50 0028:
??_C@_0CI@GPIJKLOM@?$CD?$CD?$CD?5ERROR?5unsupported?5bitmap?5gro@ (0000)
00251A78 0036:
??_C@_0DG@OFJIKCDA@?$CD?$CD?$CD?5WARNING?5tried?5to?5smooth?5an?5i@ (0000)
00251AB0 002b:
??_C@_0CL@CJDNFNLC@?$CD?$CD?$CD?5WARNING?5tried?5to?5smooth?5a?5sp@ (0000)
00251ADC 0003:
??_C@_02PCIJFNDE@?$AN?6?$AA@ (0000)
00251AE0 0038:
??_C@_0DI@GIJPOGBA@skipping?53D?5texture?5with?5non?5pow@ (0000)
00251B18 002f:
??_C@_0CP@OCGAOEDI@skipping?53D?5texture?5with?5incompa@ (0000)
00251B48 0035:
??_C@_0DF@CHABNBMJ@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5cre@ (0000)
00251B80 002b:
??_C@_0CL@KIGCKIOC@skipping?5cube?5map?5which?5spanned?5@ (0000)
00251BAC 0031:
??_C@_0DB@JFFHMOLJ@skipping?5cube?5map?5with?5incompati@ (0000)
00251BE0 002d:
??_C@_0CN@GHMACEEI@skipping?5cube?5map?5with?5less?5than@ (0000)
00251C10 002a:
??_C@_0CK@MAFGCHJA@skipping?5cube?5map?5with?5non?9squar@ (0000)
00251C3C 0012:
??_C@_0BC@OIPPCJDD@?$CBtemporary_bitmap?$AA@ (0000)
00251C50 002b:
??_C@_0CL@MNFLENMO@?$CD?$CD?$CD?5ERROR?5sprite?5budget?5exceeded@ (0000)
00251C7C 001e:
??_C@_0BO@PEKHFDDE@sprite?5budget?5met?5?$CI?$CF3?40f?$CF?$CF?$CJ?$AN?6?$AA@ (0000)
00251C9C 0023:
??_C@_0CD@PAPLMEDP@?$CD?$CD?$CD?5WARNING?5no?5sprite?5budget?5set@ (0000)
00251CC0 002e:
??_C@_0CO@CKBCALDJ@texture?5page?5created?5?$CD?$CFdx?$CD?$CFd?5?$CI?$CF3@ (0000)
00251CF0 0041:
??_C@_0EB@GJMFGLDP@?$CD?$CD?$CD?5ERROR?5extract_sprite?3?5failed@ (0000)
00251D38 0084:
??_C@_0IE@MKAIONEP@?$CD?$CD?$CD?5ERROR?5hey?5?9?5don?8t?5even?5try?5i@ (0000)
00251DC0 0044:
??_C@_0EE@DCBFAMBD@?$CD?$CD?$CD?5ERROR?5one?5or?5more?5sprites?5do@ (0000)
00251E04 0037:
??_C@_0DH@NFCBCMJC@?$CD?$CD?$CD?5WARNING?5skipped?5a?5bitmap?5whi@ (0000)
00251E3C 0034:
??_C@_0DE@MGADPMCF@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5add@ (0000)
00251E70 0097:
??_C@_0JH@OBKFAPGC@?$CD?$CD?$CD?5ERROR?5extract?3?5tried?5to?5extr@ (0000)
00251F08 003b:
??_C@_0DL@FLDEAMAO@?$CD?$CD?$CD?5ERROR?5interface?1linear?5bitma@ (0000)
00251F48 0054:
??_C@_0FE@BCCMGDBK@?$DN?$DN?$DO?5?$CB?$CBWARNING?$CB?$CB?5bitmap?5with?5grea@ (0000)
00251FA0 0052:
??_C@_0FC@GFFGPBON@?$DN?$DN?$DO?5?$CB?$CBWARNING?$CB?$CB?5usage?5set?5to?5alp@ (0000)
00251FF4 0036:
??_C@_0DG@DKLFGIJN@?$CD?$CD?$CD?5ERROR?5can?8t?5extract?5sprites?5@ (0000)
0025202C 003a:
??_C@_0DK@PBJHAOGB@?$CD?$CD?$CD?5ERROR?5can?8t?5extract?53D?5textu@ (0000)
00252068 0053:
??_C@_0FD@OJBFAEHG@?$CD?$CD?$CD?5ERROR?5extract?3?5compressed?5co@ (0000)
002520BC 0023:
??_C@_0CD@LJHCCFGP@bottom?$DM?$DNextract_data?4plate?9?$DOheig@ (0000)
002520E0 000c:
??_C@_0M@NNAAFJDK@bottom?$DO?$DNtop?$AA@ (0000)
002520EC 0007:
??_C@_06JJMFEGFL@top?$DO?$DN0?$AA@ (0000)
002520F4 002f:
??_C@_0CP@LHGGIDHB@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5all@ (0000)
00252124 0032:
??_C@_0DC@MGLFJBKA@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5all@ (0000)
00252158 0034:
??_C@_0DE@BGBBNNDB@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5dec@ (0000)
00252190 0052:
??_C@_0FC@OEKPABF@decompressed_plate_size?$DN?$DNsizeof?$CI@ (0000)
002521E4 003e:
??_C@_0DO@EDMJDNFC@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5res@ (0000)
00252224 0033:
??_C@_0DD@FAIEDAFJ@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5all@ (0000)
00252258 0040:
??_C@_0EA@PFFINKKO@group?9?$DOusage?5?$DO?$DN0?5?$CG?$CG?5group?9?$DOusage@ (0000)
00252298 0041:
??_C@_0EB@OKMLMBHL@group?9?$DOformat?$DO?$DN0?5?$CG?$CG?5group?9?$DOforma@ (0000)
002522DC 003d:
??_C@_0DN@FIMFNAFO@group?9?$DOtype?5?$DO?$DN0?5?$CG?$CG?5group?9?$DOtype?5?$DM@ (0000)
0025231C 0006:
??_C@_05MGJOOGAJ@group?$AA@ (0000)
00252324 0037:
??_C@_0DH@BHCBHGEP@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5all@ (0000)
0025235C 0032:
??_C@_0DC@MDFBMDEG@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5com@ (0000)
00252390 0031:
??_C@_0DB@KLKAOHKM@?$CD?$CD?$CD?5ERROR?5extract?3?5failed?5to?5rea@ (0000)
002523C4 001b:
??_C@_0BL@KAAMGFCF@bitmap_verify?$CIplate?0?5TRUE?$CJ?$AA@ (0000)
0031BED4 002c:
_extract_data (0000)
*/
/* ---------- headers */
#include "cseries.h"
#include "bitmaps/bitmap_group.h"
#include "bitmaps/bitmap_group_internal.h"
#include "bitmaps/bitmap_drawing.h"
#include "bitmaps/bitmaps.h"
#include "bitmaps/bitmaps_quantitize_internal.h"
#include "bitmaps/bitmap_utilities.h"
#include "cache/cache_files.h"
#include "cseries/errors.h"
#include "math/integer_math.h"
#include "memory/data.h"
#include "memory/data_compress.h"
#include "memory/texture_page.h"
/* ---------- constants */
enum
{
_bitmap_group_type_2d_textures,
_bitmap_group_type_3d_textures,
_bitmap_group_type_cube_maps,
_bitmap_group_type_sprites,
_bitmap_group_type_interface_bitmaps,
NUMBER_OF_BITMAP_GROUP_TYPES
};
enum
{
_bitmap_group_format_compressed_color_key_transparency,
_bitmap_group_format_compressed_explicit_alpha,
_bitmap_group_format_compressed_interpolated_alpha,
_bitmap_group_format_16bit_color,
_bitmap_group_format_32bit_color,
_bitmap_group_format_monochrome,
NUMBER_OF_BITMAP_GROUP_FORMATS
};
enum
{
_bitmap_group_usage_alpha_blend,
_bitmap_group_usage_default,
_bitmap_group_usage_height_map,
_bitmap_group_usage_detail_map,
_bitmap_group_usage_light_map,
_bitmap_group_usage_vector_map,
NUMBER_OF_BITMAP_GROUP_USAGES
};
enum
{
_bitmap_format_a8,
_bitmap_format_y8,
_bitmap_format_ay8,
_bitmap_format_a8y8,
_bitmap_format_unused1,
_bitmap_format_unused2,
_bitmap_format_r5g6b5,
_bitmap_format_unused3,
_bitmap_format_a1r5g5b5,
_bitmap_format_a4r4g4b4,
_bitmap_format_x8r8g8b8,
_bitmap_format_a8r8g8b8,
_bitmap_format_unused4,
_bitmap_format_unused5,
_bitmap_format_dxt1,
_bitmap_format_dxt3,
_bitmap_format_dxt5,
_bitmap_format_p8_bump,
NUMBER_OF_BITMAP_FORMATS
};
enum
{
_bitmap_type_2d,
_bitmap_type_3d,
_bitmap_type_cube_map,
};
enum
{
_bitmap_has_power_of_two_dimensions_bit,
_bitmap_compressed_bit,
_bitmap_palettized_bit,
_bitmap_swizzled_bit,
_bitmap_linear_bit,
};
enum
{
_bitmap_group_diffusion_dither_bit,
_bitmap_group_disable_vector_compression_bit,
_bitmap_group_uniform_sprite_sequences_bit,
_bitmap_group_extract_sprites_filthy_bug_fix_bit = 3,
};
enum
{
_bitmap_group_sprite_budget_32,
_bitmap_group_sprite_budget_64,
_bitmap_group_sprite_budget_128,
_bitmap_group_sprite_budget_256,
_bitmap_group_sprite_budget_512,
_bitmap_group_sprite_budget_1024,
};
enum
{
_bitmap_group_sprite_usage_blend_add_sub_max,
_bitmap_group_sprite_usage_multiply_min,
_bitmap_group_sprite_usage_double_multiply,
};
/* ---------- macros */
/* ---------- structures */
struct bitmap_extract_data
{
struct bitmap_extract_entry *bitmaps;
short bitmap_count;
pixel32 top_reference;
pixel32 bottom_reference;
pixel32 adjusted_bounds_reference;
boolean extract_sequences;
boolean single_sequence;
struct bitmap_group *group;
struct bitmap_data *plate;
long build_debug_plate;
struct bitmap_group_sequence *sequence;
short sequence_index;
short bitmap_index;
};
struct bitmap_extract_entry
{
struct bitmap_data *bitmap;
short sequence_index;
short sprite_index;
short page_index;
word unused;
long page_entry_index;
};
struct bitmap_extract_cube_map_face
{
short source_x_block;
short source_y_block;
short source_x_edge;
short source_y_edge;
short source_x_column_delta;
short source_y_column_delta;
short source_x_row_delta;
short source_y_row_delta;
};
typedef char bitmap_extract_entry_size_assert[
sizeof(struct bitmap_extract_entry) == 0x10 ? 1 : -1];
typedef char bitmap_extract_data_size_assert[
sizeof(struct bitmap_extract_data) == 0x2C ? 1 : -1];
/* ---------- prototypes */
static void extract_initialize(
void);
static short extract_find_sequence_bounds(
short *top_reference);
static void extract_warn_about_horizontal_border(
short bottom);
static boolean extract_find_bitmap_bounds(
rectangle2d const *bounds,
rectangle2d *adjusted_bounds_reference);
static short extract_get_bitmap_format(
struct bitmap_data *bitmap);
static void extract_pixels_from_mipmap(
struct bitmap_data *source_bitmap,
struct bitmap_data *destination_bitmap,
short source_mipmap_index);
struct bitmap_data *extract_build_debug_plate(
struct bitmap_data *bitmap,
boolean alpha_to_rgb,
boolean include_mipmaps,
boolean border);
static boolean extract_sequences(
void);
static boolean extract_without_sequences(
void);
static boolean extract_plateless_cube_map(
struct bitmap_data *bitmap);
static void extract_build_texture_pages_by_sequence(
struct texture_page **texture_pages,
short *texture_page_count,
short minimum_page_size,
short spacing);
static short extract_add_bitmap(
struct bitmap_data *bitmap);
static void extract_mipmaps_to_bitmap(
struct bitmap_data *source_bitmap,
struct bitmap_data *destination_bitmap);
static boolean extract_3d_textures(
void);
static boolean extract_cube_maps(
void);
static boolean extract_sprites(
void);
static boolean extract_bitmap(
rectangle2d const *bounds);
static boolean extract_sequence(
short top,
short bottom);
/* ---------- globals */
static struct bitmap_extract_data extract_data;
/* ---------- public code */
boolean bitmaps_extract(
struct bitmap_group *group,
long build_debug_plate)
{
unsigned long decompressed_plate_size;
boolean should_extract_sequences;
boolean result = TRUE;
match_assert("c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c", 0xB4, group);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0xB5,
group->type >=0 && group->type <NUMBER_OF_BITMAP_GROUP_TYPES);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0xB6,
group->format>=0 && group->format<NUMBER_OF_BITMAP_GROUP_FORMATS);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0xB7,
group->usage >=0 && group->usage <NUMBER_OF_BITMAP_GROUP_USAGES);
extract_data.bitmap_count = 0;
extract_data.bitmaps = match_malloc(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0xBA,
0x4000);
if (!extract_data.bitmaps)
{
error(_error_silent, "### ERROR extract: failed to allocate bitmap array");
result = FALSE;
}
if (!result ||
!tag_block_resize(&group->bitmaps, 0) ||
!tag_block_resize(&group->sequences, 0) ||
!tag_data_resize(&group->pixel_data, 0))
{
error(_error_silent, "### ERROR extract: failed to resize bitmap group tags to zero");
result = FALSE;
}
else
{
extract_data.sequence = NULL;
extract_data.sequence_index = NONE;
extract_data.group = group;
extract_data.plate = bitmap_2d_new(
group->import_width,
group->import_height,
0,
_bitmap_format_a8r8g8b8);
if (extract_data.plate)
{
decompressed_plate_size = data_decompressed_size(
group->import_bitmap.address,
group->import_bitmap.size);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x104,
decompressed_plate_size==sizeof(pixel32)*group->import_width*group->import_height);
if (data_decompress(
group->import_bitmap.address,
group->import_bitmap.size,
bitmap_mipmap_address(extract_data.plate, 0),
&decompressed_plate_size,
decompressed_plate_size))
{
extract_initialize();
should_extract_sequences = extract_data.extract_sequences;
extract_data.build_debug_plate = build_debug_plate;
if (should_extract_sequences)
result = extract_sequences();
else
result = extract_without_sequences();
bitmap_delete(extract_data.plate);
}
else
{
error(_error_silent, "### ERROR extract: failed to decompress color plate");
result = FALSE;
}
}
else
{
error(_error_silent, "### ERROR extract: failed to allocate color plate");
result = FALSE;
}
}
if (result)
{
switch (extract_data.group->type)
{
case _bitmap_group_type_2d_textures:
case _bitmap_group_type_interface_bitmaps:
break;
case _bitmap_group_type_3d_textures:
result = extract_3d_textures();
break;
case _bitmap_group_type_cube_maps:
result = extract_cube_maps();
break;
case _bitmap_group_type_sprites:
result = extract_sprites();
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x137,
FALSE,
"### ERROR unsupported bitmap group type");
}
}
if (extract_data.bitmaps)
{
match_free(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x13D,
extract_data.bitmaps);
}
return result;
}
boolean bitmaps_extract_from_plate(
struct bitmap_data *plate,
struct bitmap_group *group,
long build_debug_plate)
{
long compressed_color_plate_size;
void *compressed_color_plate;
match_assert("c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c", 0x83, bitmap_verify(plate, TRUE));
match_assert("c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c", 0x84, group);
compressed_color_plate_size = bitmap_get_pixel_data_size(plate);
group->import_width = plate->width;
group->import_height = plate->height;
group->import_bitmap.address = match_malloc(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x8A,
compressed_color_plate_size);
if (group->import_bitmap.address)
{
if (data_compress(
bitmap_mipmap_address(plate, 0),
compressed_color_plate_size,
group->import_bitmap.address,
&compressed_color_plate_size,
compressed_color_plate_size))
{
compressed_color_plate = match_realloc(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x90,
group->import_bitmap.address,
compressed_color_plate_size);
if (compressed_color_plate)
{
group->import_bitmap.address = compressed_color_plate;
group->import_bitmap.size = compressed_color_plate_size;
return bitmaps_extract(group, build_debug_plate);
}
error(_error_silent, "### ERROR extract: failed to realloc color plate");
return FALSE;
}
error(_error_silent, "### ERROR extract: failed to compress color plate");
return FALSE;
}
error(_error_silent, "### ERROR extract: failed to allocate temporary buffer");
return FALSE;
}
/* ---------- private code */
static void extract_initialize(
void)
{
short x;
extract_data.extract_sequences = TRUE;
extract_data.single_sequence = FALSE;
extract_data.top_reference =
*(pixel32 *)bitmap_2d_address(extract_data.plate, 0, 0, 0) & 0xFFFFFF;
extract_data.bottom_reference =
*(pixel32 *)bitmap_2d_address(extract_data.plate, 1, 0, 0) & 0xFFFFFF;
extract_data.adjusted_bounds_reference =
*(pixel32 *)bitmap_2d_address(extract_data.plate, 2, 0, 0) & 0xFFFFFF;
if (extract_data.adjusted_bounds_reference == extract_data.bottom_reference &&
extract_data.bottom_reference != 0xFF)
{
extract_data.extract_sequences = FALSE;
}
if (extract_data.top_reference == extract_data.bottom_reference)
{
extract_data.adjusted_bounds_reference = 0xFFFF;
extract_data.single_sequence = TRUE;
}
for (x = 3; x < extract_data.plate->width; x++)
{
pixel32 top = *(pixel32 *)bitmap_2d_address(extract_data.plate, x, 0, 0) & 0xFFFFFF;
pixel32 bottom = *(pixel32 *)bitmap_2d_address(extract_data.plate, x, 1, 0) & 0xFFFFFF;
if (top != extract_data.top_reference &&
bottom != extract_data.bottom_reference)
{
extract_data.extract_sequences = FALSE;
}
}
if (!extract_data.extract_sequences)
{
extract_data.adjusted_bounds_reference = 0xFF000000;
extract_data.bottom_reference = 0xFF000000;
extract_data.top_reference = 0xFF000000;
}
return;
}
static short extract_find_sequence_bounds(
short *top_reference)
{
short bottom;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x1D9,
top_reference);
if (extract_data.single_sequence)
{
boolean found_sequence = FALSE;
for (bottom = *top_reference; bottom < extract_data.plate->height; bottom++)
{
short x;
boolean found_sequence_pixel = FALSE;
for (x = 0; x < extract_data.plate->width; x++)
{
pixel32 color = *(pixel32 *)bitmap_2d_address(
extract_data.plate,
x,
bottom,
0) & 0xFFFFFF;
if (color != extract_data.top_reference)
found_sequence_pixel = TRUE;
}
if (found_sequence_pixel)
{
found_sequence = TRUE;
}
else if (found_sequence)
{
break;
}
else
{
*top_reference = bottom + 1;
}
}
}
else
{
boolean found_top_reference = FALSE;
for (bottom = *top_reference; bottom < extract_data.plate->height; bottom++)
{
pixel32 color = *(pixel32 *)bitmap_2d_address(
extract_data.plate,
0,
bottom,
0) & 0xFFFFFF;
if (color == extract_data.top_reference)
{
found_top_reference = TRUE;
}
else if (color == extract_data.bottom_reference && found_top_reference)
{
break;
}
else
{
*top_reference = bottom + 1;
}
}
}
return bottom;
}
static void extract_warn_about_horizontal_border(
short bottom)
{
if (VALID_INDEX(bottom, extract_data.plate->height))
{
short x;
for (x = 0; x < extract_data.plate->width; x++)
{
if ((*(pixel32 *)bitmap_2d_address(extract_data.plate, x, bottom, 0) & 0xFFFFFF) !=
extract_data.bottom_reference)
{
fprintf(stdout, "### WARNING horizontal border broken at (#%d,#%d)\r\n", x, bottom);
fflush(stdout);
break;
}
}
}
return;
}
static boolean extract_plateless_cube_map(
struct bitmap_data *bitmap)
{
boolean result = TRUE;
short face_size;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x2C2,
bitmap_verify(bitmap, TRUE));
if (!(bitmap->width % 4) &&
bitmap->height >= 3 * (bitmap->width / 4) &&
!(bitmap->width & (bitmap->width - 1)))
{
face_size = bitmap->width / 4;
if (extract_data.bitmap_count + 6 <= 0x400)
{
struct bitmap_extract_cube_map_face faces[6] =
{
{ 0, 1, 1, 0, 0, 1, -1, 0 },
{ 1, 1, 1, 1, -1, 0, 0, -1 },
{ 2, 1, 0, 1, 0, -1, 1, 0 },
{ 3, 1, 0, 0, 1, 0, 0, 1 },
{ 0, 0, 1, 0, 0, 1, -1, 0 },
{ 0, 2, 1, 0, 0, 1, -1, 0 },
};
short face_index;
for (face_index = 0; face_index < NUMBEROF(faces); face_index++)
{
struct bitmap_extract_entry *entry =
&extract_data.bitmaps[extract_data.bitmap_count++];
entry->bitmap = bitmap_2d_new(
face_size,
face_size,
0,
_bitmap_format_a8r8g8b8);
if (entry->bitmap)
{
short destination_y;
for (destination_y = 0; destination_y < face_size; destination_y++)
{
short source_x =
faces[face_index].source_x_block * face_size +
faces[face_index].source_x_edge * (face_size - 1) +
destination_y * faces[face_index].source_x_row_delta;
short source_y =
faces[face_index].source_y_block * face_size +
faces[face_index].source_y_edge * (face_size - 1) +
destination_y * faces[face_index].source_y_row_delta;
short destination_x;
for (destination_x = 0; destination_x < face_size; destination_x++)
{
*(pixel32 *)bitmap_2d_address(
entry->bitmap,
destination_x,
destination_y,
0) = *(pixel32 *)bitmap_2d_address(
bitmap,
source_x,
source_y,
0);
source_x += faces[face_index].source_x_column_delta;
source_y += faces[face_index].source_y_column_delta;
}
}
entry->sequence_index = extract_data.sequence_index;
entry->sprite_index = NONE;
entry->page_index = NONE;
entry->page_entry_index = NONE;
}
else
{
error(_error_silent, "### ERROR extract: failed to allocate temporary bitmap");
result = FALSE;
}
}
}
else
{
error(
_error_silent,
"### ERROR extract: can't handle more than (#%d) temporary bitmaps",
0x400);
result = FALSE;
}
}
else
{
error(
_error_silent,
"### ERROR extract: plateless cube map had invalid dimensions #%dx#%d",
bitmap->width,
bitmap->height);
result = FALSE;
}
return result;
}
static boolean extract_find_bitmap_bounds(
rectangle2d const *bounds,
rectangle2d *adjusted_bounds_reference)
{
boolean result = FALSE;
short y;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x3F7,
bounds);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x3F8,
adjusted_bounds_reference);
adjusted_bounds_reference->y0 = SHORT_MAX;
adjusted_bounds_reference->x0 = SHORT_MAX;
adjusted_bounds_reference->y1 = SHORT_MIN;
adjusted_bounds_reference->x1 = SHORT_MIN;
for (y = bounds->y0; y < bounds->y1; y++)
{
short x;
for (x = bounds->x0; x < bounds->x1; x++)
{
if (VALID_INDEX(x, extract_data.plate->width) &&
VALID_INDEX(y, extract_data.plate->height))
{
pixel32 color = *(pixel32 *)bitmap_2d_address(extract_data.plate, x, y, 0);
pixel32 rgb = color & 0xFFFFFF;
boolean contains_data = TRUE;
if (extract_data.extract_sequences)
{
if (rgb == extract_data.top_reference ||
rgb == extract_data.bottom_reference ||
rgb == extract_data.adjusted_bounds_reference)
{
contains_data = FALSE;
}
else if (extract_data.group->usage == _bitmap_group_usage_alpha_blend &&
!(color & 0xFF000000))
{
contains_data = FALSE;
}
}
if (contains_data)
{
adjusted_bounds_reference->x0 = MIN(x, adjusted_bounds_reference->x0);
adjusted_bounds_reference->y0 = MIN(y, adjusted_bounds_reference->y0);
adjusted_bounds_reference->x1 = MAX(x, adjusted_bounds_reference->x1);
adjusted_bounds_reference->y1 = MAX(y, adjusted_bounds_reference->y1);
result = TRUE;
}
}
}
}
adjusted_bounds_reference->x1++;
adjusted_bounds_reference->y1++;
return result;
}
static short extract_get_bitmap_format(
struct bitmap_data *bitmap)
{
short format = NONE;
short alpha_bits = 0;
short color_bits = 0;
boolean channels_differ = FALSE;
pixel32 *pixels;
pixel32 first_pixel;
long pixel_count;
long pixel_index;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x429,
bitmap_verify(bitmap, TRUE));
pixels = bitmap_mipmap_address(bitmap, 0);
first_pixel = *pixels;
pixel_count = bitmap_get_pixel_count(bitmap);
for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
{
pixel32 pixel = pixels[pixel_index];
switch (pixel >> 24)
{
case 0:
if ((first_pixel & 0xFF000000) == 0xFF000000)
alpha_bits = MAX(alpha_bits, 1);
break;
case 0xFF:
if (!(first_pixel & 0xFF000000))
alpha_bits = MAX(alpha_bits, 1);
break;
default:
alpha_bits = 8;
break;
}
switch ((pixel >> 16) & 0xFF)
{
case 0:
if ((first_pixel & 0x00FF0000) == 0x00FF0000)
color_bits = MAX(color_bits, 1);
break;
case 0xFF:
if (!(first_pixel & 0x00FF0000))
color_bits = MAX(color_bits, 1);
break;
default:
color_bits = 8;
break;
}
if ((pixel >> 24) != ((pixel >> 16) & 0xFF))
channels_differ = TRUE;
}
switch (extract_data.group->format)
{
case _bitmap_group_format_compressed_color_key_transparency:
format = _bitmap_format_dxt1;
break;
case _bitmap_group_format_compressed_explicit_alpha:
format = alpha_bits > 0 ? _bitmap_format_dxt3 : _bitmap_format_dxt1;
break;
case _bitmap_group_format_compressed_interpolated_alpha:
format = alpha_bits > 0 ? _bitmap_format_dxt5 : _bitmap_format_dxt1;
break;
case _bitmap_group_format_16bit_color:
if (alpha_bits == 0)
format = _bitmap_format_r5g6b5;
else if (alpha_bits == 1)
format = _bitmap_format_a1r5g5b5;
else
format = _bitmap_format_a4r4g4b4;
break;
case _bitmap_group_format_32bit_color:
format = alpha_bits == 0 ? _bitmap_format_x8r8g8b8 : _bitmap_format_a8r8g8b8;
break;
case _bitmap_group_format_monochrome:
if (alpha_bits == 0)
format = _bitmap_format_y8;
else if (color_bits == 0)
format = _bitmap_format_a8;
else
format = channels_differ ? _bitmap_format_a8y8 : _bitmap_format_ay8;
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x466,
FALSE,
"### ERROR extract: unsupported bitmap group format");
break;
}
if (extract_data.group->type == _bitmap_group_type_interface_bitmaps)
{
switch (format)
{
case _bitmap_format_a1r5g5b5:
format = _bitmap_format_a4r4g4b4;
break;
case _bitmap_format_x8r8g8b8:
format = _bitmap_format_a8r8g8b8;
break;
}
}
if (extract_data.group->usage == _bitmap_group_usage_height_map ||
extract_data.group->usage == _bitmap_group_usage_vector_map)
{
if (!TEST_FLAG(
extract_data.group->flags,
_bitmap_group_disable_vector_compression_bit))
{
format = _bitmap_format_p8_bump;
}
}
return format;
}
static void extract_pixels_to_mipmap(
struct bitmap_data *source_bitmap,
struct bitmap_data *destination_bitmap,
short destination_mipmap_index)
{
pixel32 *source_pixels;
void *destination_pixels;
long pixel_count;
long pixel_index;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6A6,
bitmap_verify(source_bitmap, TRUE));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6A7,
source_bitmap->width ==MAX(1, destination_bitmap->width >>destination_mipmap_index));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6A8,
source_bitmap->height==MAX(1, destination_bitmap->height>>destination_mipmap_index));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6A9,
source_bitmap->depth ==MAX(1, destination_bitmap->depth >>destination_mipmap_index));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6AB,
bitmap_verify(destination_bitmap, FALSE));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6AC,
destination_bitmap->type==source_bitmap->type);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6AD,
destination_mipmap_index>=0 && destination_mipmap_index<=destination_bitmap->mipmap_count);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6AE,
!TEST_FLAG(destination_bitmap->flags, _bitmap_swizzled_bit));
if (TEST_FLAG(destination_bitmap->flags, _bitmap_compressed_bit))
{
bitmap_compress_to_mipmap(
source_bitmap,
destination_bitmap,
destination_mipmap_index,
extract_data.extract_sequences ? &extract_data.adjusted_bounds_reference : NULL);
return;
}
source_pixels = bitmap_mipmap_address(source_bitmap, 0);
destination_pixels = bitmap_mipmap_address(
destination_bitmap,
destination_mipmap_index);
pixel_count = bitmap_get_pixel_count(source_bitmap);
for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
{
pixel32 pixel = source_pixels[pixel_index];
switch (destination_bitmap->format)
{
case _bitmap_format_r5g6b5:
((word *)destination_pixels)[pixel_index] = (word)(
((((pixel >> 16) & 0xFF) >> 3) << 11) |
((((pixel >> 8) & 0xFF) >> 2) << 5) |
((pixel & 0xFF) >> 3));
break;
case _bitmap_format_a1r5g5b5:
((word *)destination_pixels)[pixel_index] = (word)(
(((pixel >> 24) ? 0x80 : 0) << 8) |
((((pixel >> 16) & 0xFF) >> 3) << 10) |
((((pixel >> 8) & 0xFF) >> 3) << 5) |
((pixel & 0xFF) >> 3));
break;
case _bitmap_format_a4r4g4b4:
((word *)destination_pixels)[pixel_index] = (word)(
(((pixel >> 24) >> 4) << 12) |
((((pixel >> 16) & 0xFF) >> 4) << 8) |
((((pixel >> 8) & 0xFF) >> 4) << 4) |
((pixel & 0xFF) >> 4));
break;
case _bitmap_format_x8r8g8b8:
((pixel32 *)destination_pixels)[pixel_index] = pixel | 0xFF000000;
break;
case _bitmap_format_a8r8g8b8:
((pixel32 *)destination_pixels)[pixel_index] = pixel;
break;
case _bitmap_format_a8:
((byte *)destination_pixels)[pixel_index] = (byte)(pixel >> 24);
break;
case _bitmap_format_y8:
case _bitmap_format_ay8:
((byte *)destination_pixels)[pixel_index] = (byte)(pixel >> 16);
break;
case _bitmap_format_a8y8:
((word *)destination_pixels)[pixel_index] = (word)(pixel >> 16);
break;
case _bitmap_format_p8_bump:
((byte *)destination_pixels)[pixel_index] =
(byte)palette_find_closest_match(global_vector_palette, pixel);
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6EC,
FALSE,
"### ERROR unsupported bitmap format");
break;
}
}
return;
}
static void extract_pixels_from_mipmap(
struct bitmap_data *source_bitmap,
struct bitmap_data *destination_bitmap,
short source_mipmap_index)
{
void *source_pixels;
pixel32 *destination_pixels;
long pixel_count;
long pixel_index;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6F9,
bitmap_verify(destination_bitmap, TRUE));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6FA,
destination_bitmap->width ==MAX(1, source_bitmap->width >>source_mipmap_index));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6FB,
destination_bitmap->height==MAX(1, source_bitmap->height>>source_mipmap_index));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6FC,
destination_bitmap->depth ==MAX(1, source_bitmap->depth >>source_mipmap_index));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6FE,
bitmap_verify(source_bitmap, FALSE));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x6FF,
source_bitmap->type==destination_bitmap->type);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x700,
source_mipmap_index>=0 && source_mipmap_index<=source_bitmap->mipmap_count);
if (TEST_FLAG(source_bitmap->flags, _bitmap_compressed_bit))
{
bitmap_uncompress_from_mipmap(
source_bitmap,
destination_bitmap,
source_mipmap_index);
return;
}
source_pixels = bitmap_mipmap_address(source_bitmap, source_mipmap_index);
destination_pixels = bitmap_mipmap_address(destination_bitmap, 0);
pixel_count = bitmap_get_pixel_count(destination_bitmap);
for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
{
destination_pixels[pixel_index] = bitmap_format_to_a8r8g8b8(
source_bitmap->format,
source_pixels,
pixel_index);
}
return;
}
static void extract_build_texture_pages_by_sequence(
struct texture_page **texture_pages,
short *texture_page_count,
short minimum_page_size,
short spacing)
{
short page_count = 0;
short spanned_page_count = 1;
short sequence_index;
for (sequence_index = 0;
sequence_index < extract_data.group->sequences.count;
sequence_index++)
{
short first_bitmap_index = 0;
short page_index = 0;
boolean page_complete;
do
{
struct texture_page *texture_page;
boolean new_page;
short bitmap_index;
if (page_index < page_count)
{
texture_page = texture_pages[page_index];
new_page = FALSE;
}
else if (page_count < 32)
{
texture_page = NULL;
new_page = TRUE;
}
else
{
break;
}
if (texture_page)
texture_page_textures_begin(texture_page);
page_complete = TRUE;
for (bitmap_index = first_bitmap_index;
bitmap_index < extract_data.bitmap_count;
bitmap_index++)
{
struct bitmap_extract_entry *entry = &extract_data.bitmaps[bitmap_index];
if (entry->sequence_index == sequence_index)
{
long page_entry_index;
if (!texture_page)
{
short page_width = MAX(minimum_page_size, entry->bitmap->width);
short page_height = MAX(minimum_page_size, entry->bitmap->height);
page_width = (short)MIN(512, ceiling_power2(page_width));
page_height = (short)MIN(512, ceiling_power2(page_height));
texture_page = texture_page_new(NULL, page_width, page_height, spacing);
if (!texture_page)
break;
texture_page_textures_begin(texture_page);
texture_pages[page_count++] = texture_page;
}
page_entry_index = texture_page_texture_new(
texture_page,
entry->bitmap->width,
entry->bitmap->height,
TRUE);
if (page_entry_index != NONE)
{
entry->page_index = page_index;
entry->page_entry_index = page_entry_index;
}
else
{
if (new_page)
{
spanned_page_count++;
first_bitmap_index = bitmap_index;
texture_page_textures_end(texture_page);
}
else
{
texture_page_textures_cancel(texture_page);
}
page_complete = FALSE;
break;
}
}
}
if (texture_page && page_complete)
texture_page_textures_end(texture_page);
page_index++;
}
while (!page_complete);
}
for (sequence_index = 0; sequence_index < page_count; sequence_index++)
{
struct texture_page *texture_page = texture_pages[sequence_index];
short width;
short height;
do
{
width = texture_page->width >> 1;
height = texture_page->height >> 1;
}
while (width >= 32 &&
height >= 32 &&
texture_page_resize(texture_page, width, height));
}
fprintf(stdout, "sequence spanned %d texture pages\r\n", spanned_page_count);
fflush(stdout);
*texture_page_count = page_count;
return;
}
struct bitmap_data *extract_build_debug_plate(
struct bitmap_data *bitmap,
boolean alpha_to_rgb,
boolean include_mipmaps,
boolean border)
{
struct bitmap_data *converted_bitmap = NULL;
struct bitmap_data *debug_bitmap = NULL;
short mipmap_index;
short slice_count;
switch (bitmap->type)
{
case _bitmap_type_2d:
converted_bitmap = bitmap_2d_new(
bitmap->width,
bitmap->height,
bitmap->mipmap_count,
_bitmap_format_a8r8g8b8);
break;
case _bitmap_type_3d:
converted_bitmap = bitmap_3d_new(
bitmap->width,
bitmap->height,
bitmap->depth,
bitmap->mipmap_count,
_bitmap_format_a8r8g8b8);
break;
case _bitmap_type_cube_map:
converted_bitmap = bitmap_cube_map_new(
bitmap->width,
bitmap->mipmap_count,
_bitmap_format_a8r8g8b8);
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x518,
FALSE,
"### ERROR unsupported bitmap type");
break;
}
if (converted_bitmap && converted_bitmap->base_address)
{
short debug_width;
short debug_height;
for (mipmap_index = 0; mipmap_index <= bitmap->mipmap_count; mipmap_index++)
{
struct bitmap_data *mipmap_bitmap = NULL;
short width = MAX(1, bitmap->width >> mipmap_index);
short height = MAX(1, bitmap->height >> mipmap_index);
short depth = MAX(1, bitmap->depth >> mipmap_index);
switch (bitmap->type)
{
case _bitmap_type_2d:
mipmap_bitmap = bitmap_2d_new(width, height, 0, _bitmap_format_a8r8g8b8);
break;
case _bitmap_type_3d:
mipmap_bitmap = bitmap_3d_new(width, height, depth, 0, _bitmap_format_a8r8g8b8);
break;
case _bitmap_type_cube_map:
mipmap_bitmap = bitmap_cube_map_new(width, 0, _bitmap_format_a8r8g8b8);
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x542,
FALSE,
"### ERROR unsupported bitmap type");
break;
}
if (mipmap_bitmap && mipmap_bitmap->base_address)
{
extract_pixels_from_mipmap(bitmap, mipmap_bitmap, mipmap_index);
extract_pixels_to_mipmap(mipmap_bitmap, converted_bitmap, mipmap_index);
}
bitmap_delete(mipmap_bitmap);
}
switch (converted_bitmap->type)
{
case _bitmap_type_2d:
slice_count = 1;
break;
case _bitmap_type_3d:
slice_count = converted_bitmap->depth;
break;
case _bitmap_type_cube_map:
slice_count = 6;
break;
/* slice_count is left unassigned only by this default arm. Not reached unassigned: the
* arm's assertion failure calls system_exit, which does not return in January
* (0x47c960 jumps to halt_and_catch_fire 0x4f21c0, which loops or calls exit).
* Source-policy approval pending (2026-09-27 audit). */
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x567,
FALSE,
"### ERROR unsupported bitmap type");
break;
}
if (border)
{
debug_width = (converted_bitmap->width + 3) * slice_count + 3;
debug_height = converted_bitmap->height + 8;
}
else
{
debug_width = converted_bitmap->width * slice_count;
debug_height = converted_bitmap->height;
}
if (include_mipmaps)
{
for (mipmap_index = 1; mipmap_index <= converted_bitmap->mipmap_count; mipmap_index++)
{
debug_height += (converted_bitmap->height >> mipmap_index) + (border ? 4 : 0);
}
}
debug_bitmap = bitmap_2d_new(
debug_width,
debug_height,
0,
_bitmap_format_a8r8g8b8);
if (debug_bitmap && debug_bitmap->base_address)
{
short destination_y = border ? 4 : 0;
pixel32 *pixels = bitmap_mipmap_address(debug_bitmap, 0);
long pixel_count = bitmap_get_pixel_count(debug_bitmap);
long pixel_index;
for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
{
pixels[pixel_index] = 0x000000FF;
}
for (mipmap_index = 0;
mipmap_index <= (include_mipmaps ? converted_bitmap->mipmap_count : 0);
mipmap_index++)
{
short destination_x = border ? 3 : 0;
struct bitmap_data *slice_bitmap = bitmap_2d_new(
MAX(1, converted_bitmap->width >> mipmap_index),
MAX(1, converted_bitmap->height >> mipmap_index),
0,
_bitmap_format_a8r8g8b8);
if (slice_bitmap && slice_bitmap->base_address)
{
short mipmap_slice_count;
short slice_index;
switch (converted_bitmap->type)
{
case _bitmap_type_2d:
mipmap_slice_count = 1;
break;
case _bitmap_type_3d:
mipmap_slice_count = MAX(1, converted_bitmap->depth >> mipmap_index);
break;
case _bitmap_type_cube_map:
mipmap_slice_count = 6;
break;
/* mipmap_slice_count is left unassigned only by this default arm. Not reached unassigned: the
* arm's assertion failure calls system_exit, which does not return in January
* (0x47c960 jumps to halt_and_catch_fire 0x4f21c0, which loops or calls exit).
* Source-policy approval pending (2026-09-27 audit). */
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5AC,
FALSE,
"### ERROR unsupported bitmap type");
break;
}
for (slice_index = 0; slice_index < mipmap_slice_count; slice_index++)
{
point2d destination_point;
switch (converted_bitmap->type)
{
case _bitmap_type_2d:
csmemcpy(
bitmap_mipmap_address(slice_bitmap, 0),
bitmap_mipmap_address(converted_bitmap, mipmap_index),
bitmap_get_pixel_data_size(slice_bitmap));
break;
case _bitmap_type_3d:
csmemcpy(
bitmap_mipmap_address(slice_bitmap, 0),
bitmap_3d_address(converted_bitmap, 0, 0, slice_index, mipmap_index),
bitmap_get_pixel_data_size(slice_bitmap));
break;
case _bitmap_type_cube_map:
csmemcpy(
bitmap_mipmap_address(slice_bitmap, 0),
bitmap_cube_map_address(converted_bitmap, 0, 0, slice_index, mipmap_index),
bitmap_get_pixel_data_size(slice_bitmap));
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5C8,
FALSE,
"### ERROR unsupported bitmap type");
break;
}
set_point2d(&destination_point, destination_x, destination_y);
bitmap_copy(
debug_bitmap,
&destination_point,
NULL,
slice_bitmap,
NULL,
0xFFFFFFFF,
0);
destination_x += (converted_bitmap->width >> mipmap_index) + (border ? 3 : 0);
}
}
else
{
error(_error_silent, "### ERROR failed to allocate debug slice bitmap");
}
bitmap_delete(slice_bitmap);
destination_y += (converted_bitmap->height >> mipmap_index) + (border ? 4 : 0);
}
}
else
{
error(_error_silent, "### ERROR failed to allocate debug plate bitmap");
}
}
else
{
error(_error_silent, "### ERROR failed to allocate debug bitmap");
}
if (debug_bitmap && debug_bitmap->base_address && alpha_to_rgb)
{
bitmap_alpha_to_rgb(debug_bitmap);
}
bitmap_delete(converted_bitmap);
return debug_bitmap;
}
static void extract_mipmaps_to_bitmap(
struct bitmap_data *source_bitmap,
struct bitmap_data *destination_bitmap)
{
short mipmap_index;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5F9,
bitmap_verify(source_bitmap, TRUE));
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5FA,
destination_bitmap);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5FB,
destination_bitmap->base_address);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5FC,
destination_bitmap->type==source_bitmap->type);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5FD,
destination_bitmap->width==source_bitmap->width);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5FE,
destination_bitmap->height==source_bitmap->height);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x5FF,
destination_bitmap->depth==source_bitmap->depth);
for (mipmap_index = 0;
mipmap_index <= destination_bitmap->mipmap_count;
mipmap_index++)
{
boolean ignore_transparent_pixels =
extract_data.group->usage == _bitmap_group_usage_alpha_blend &&
destination_bitmap->format != _bitmap_format_y8 &&
destination_bitmap->format != _bitmap_format_r5g6b5 &&
destination_bitmap->format != _bitmap_format_x8r8g8b8;
real mipmap_fraction = (real)mipmap_index / destination_bitmap->mipmap_count;
short alpha_bias = (short)floor(
PIN(extract_data.group->alpha_bias, -1.0f, 1.0f) *
mipmap_fraction * 255.0f + 0.5f);
struct bitmap_data *mipmap_bitmap;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x616,
alpha_bias>=-255 && alpha_bias<=255);
mipmap_bitmap = bitmap_shrink(
source_bitmap,
(short)(1 << mipmap_index),
alpha_bias,
ignore_transparent_pixels);
if (mipmap_bitmap && mipmap_bitmap->base_address)
{
bitmap_sharpen(mipmap_bitmap, extract_data.group->sharpen_amount);
if (extract_data.group->usage == _bitmap_group_usage_detail_map)
{
real mipmap_count = destination_bitmap->mipmap_count;
real detail_fade = extract_data.group->detail_fade;
real fade_amount = PIN(
(real)mipmap_index / (mipmap_count * (1.0f - detail_fade) + detail_fade),
0.0f,
1.0f);
bitmap_fade(mipmap_bitmap, 0xFF7F7F7F, fade_amount);
}
if (ignore_transparent_pixels)
bitmap_alpha_bleed(mipmap_bitmap, 1);
if (extract_data.group->usage == _bitmap_group_usage_height_map)
bitmap_height_map(mipmap_bitmap, extract_data.group->bump_height);
if (extract_data.group->usage == _bitmap_group_usage_vector_map)
bitmap_vector_map(mipmap_bitmap);
if (TEST_FLAG(
extract_data.group->flags,
_bitmap_group_diffusion_dither_bit) &&
extract_data.group->usage != _bitmap_group_usage_height_map &&
extract_data.group->usage != _bitmap_group_usage_light_map &&
extract_data.group->usage != _bitmap_group_usage_vector_map)
{
short const *bits_per_channel;
switch (destination_bitmap->format)
{
case _bitmap_format_r5g6b5:
bits_per_channel = bits_per_channel_r5g6b5;
break;
case _bitmap_format_a1r5g5b5:
bits_per_channel = bits_per_channel_a1r5g5b5;
break;
case _bitmap_format_a4r4g4b4:
bits_per_channel = bits_per_channel_a4r4g4b4;
break;
default:
bits_per_channel = NULL;
break;
}
bitmap_quantitize(mipmap_bitmap, bits_per_channel);
}
extract_pixels_to_mipmap(
mipmap_bitmap,
destination_bitmap,
mipmap_index);
bitmap_delete(mipmap_bitmap);
}
else
{
error(_error_silent, "### ERROR extract: failed to allocate temporary bitmap");
}
}
if (extract_data.build_debug_plate)
{
bitmap_delete(extract_build_debug_plate(destination_bitmap, FALSE, TRUE, TRUE));
}
return;
}
static short extract_add_bitmap(
struct bitmap_data *bitmap)
{
short format;
short mipmap_count;
short bitmap_index;
struct bitmap_data *destination_bitmap;
struct bitmap_data *working_bitmap;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x487,
bitmap_verify(bitmap, TRUE));
format = extract_get_bitmap_format(bitmap);
if (extract_data.group->type == _bitmap_group_type_interface_bitmaps ||
extract_data.group->usage == _bitmap_group_usage_light_map)
{
mipmap_count = 0;
}
else
{
mipmap_count = bitmap_get_max_mipmap_count(bitmap);
if (extract_data.group->type == _bitmap_group_type_sprites && mipmap_count >= 2)
mipmap_count = 2;
if (extract_data.group->mipmap_count > 0)
{
mipmap_count = MIN(extract_data.group->mipmap_count - 1, mipmap_count);
}
}
bitmap_index = bitmap_group_add_bitmap(
extract_data.group,
bitmap->width,
bitmap->height,
bitmap->depth,
bitmap->type,
format,
mipmap_count);
extract_data.bitmap_index = bitmap_index;
if (bitmap_index != NONE)
{
working_bitmap = bitmap_clone(bitmap);
destination_bitmap = TAG_BLOCK_GET_ELEMENT(
&extract_data.group->bitmaps,
bitmap_index,
struct bitmap_data);
if (TEST_FLAG(
extract_data.group->flags,
_bitmap_group_extract_sprites_filthy_bug_fix_bit))
{
destination_bitmap->registration_point.x =
(bitmap->registration_point.x + 1) / 2;
destination_bitmap->registration_point.y =
(bitmap->registration_point.y + 1) / 2;
}
else
{
destination_bitmap->registration_point = bitmap->registration_point;
}
if (working_bitmap && working_bitmap->base_address)
{
if (extract_data.group->smoothing_filter_size > 0.0f)
{
switch (extract_data.group->type)
{
case _bitmap_group_type_2d_textures:
case _bitmap_group_type_3d_textures:
case _bitmap_group_type_cube_maps:
bitmap_smooth(
working_bitmap,
extract_data.group->smoothing_filter_size);
break;
case _bitmap_group_type_sprites:
fprintf(stdout, "### WARNING tried to smooth a sprite group", "\r\n");
fflush(stdout);
break;
case _bitmap_group_type_interface_bitmaps:
fprintf(stdout, "### WARNING tried to smooth an interface-bitmap group", "\r\n");
fflush(stdout);
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x4D0,
FALSE,
"### ERROR unsupported bitmap group type");
break;
}
}
extract_mipmaps_to_bitmap(working_bitmap, destination_bitmap);
if (destination_bitmap->type == _bitmap_type_3d)
{
fprintf(
stdout,
"bitmap created: #%dx#%dx#%d, %s, %dK-bytes\r\n",
destination_bitmap->width,
destination_bitmap->height,
destination_bitmap->depth,
bitmap_format_get_string(destination_bitmap->format),
bitmap_get_pixel_data_size(destination_bitmap) / 1024);
fflush(stdout);
}
else
{
fprintf(
stdout,
"bitmap created: #%dx#%d, %s, %dK-bytes\r\n",
destination_bitmap->width,
destination_bitmap->height,
bitmap_format_get_string(destination_bitmap->format),
bitmap_get_pixel_data_size(destination_bitmap) / 1024);
fflush(stdout);
}
}
}
return bitmap_index;
}
static boolean extract_bitmap(
rectangle2d const *bounds)
{
boolean result = TRUE;
boolean warned_about_dxt1_alpha = FALSE;
boolean warned_about_zero_alpha = FALSE;
rectangle2d adjusted_bounds;
struct bitmap_data *bitmap;
short source_y;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x322,
bounds);
if (extract_find_bitmap_bounds(bounds, &adjusted_bounds))
{
bitmap = bitmap_2d_new(
adjusted_bounds.x1 - adjusted_bounds.x0,
adjusted_bounds.y1 - adjusted_bounds.y0,
0,
_bitmap_format_a8r8g8b8);
if (bitmap)
{
if (TEST_FLAG(
extract_data.group->flags,
_bitmap_group_extract_sprites_filthy_bug_fix_bit))
{
bitmap->registration_point.x =
bounds->x0 + bounds->x1 - 2 * adjusted_bounds.x0;
bitmap->registration_point.y =
bounds->y0 + bounds->y1 - 2 * adjusted_bounds.y0;
}
else
{
bitmap->registration_point.x =
(bounds->x0 + bounds->x1) / 2 - adjusted_bounds.x0;
bitmap->registration_point.y =
(bounds->y0 + bounds->y1) / 2 - adjusted_bounds.y0;
}
for (source_y = adjusted_bounds.y0; source_y < adjusted_bounds.y1; source_y++)
{
pixel32 *destination = bitmap_2d_address(bitmap, 0, source_y - adjusted_bounds.y0, 0);
short source_x;
for (source_x = adjusted_bounds.x0; source_x < adjusted_bounds.x1; source_x++)
{
pixel32 color = *(pixel32 *)bitmap_2d_address(
extract_data.plate,
source_x,
source_y,
0);
if (extract_data.extract_sequences)
{
pixel32 rgb = color & 0xFFFFFF;
if (rgb == extract_data.top_reference ||
rgb == extract_data.adjusted_bounds_reference ||
rgb == extract_data.bottom_reference)
{
color = 0;
}
}
if (extract_data.group->usage == _bitmap_group_usage_alpha_blend &&
!(color & 0xFF000000) &&
(color & 0xFFFFFF) &&
!warned_about_zero_alpha)
{
fprintf(
stdout,
"==> !!WARNING!! usage set to alpha; non-zero color overlaps with zero-alpha <==\r\n");
fflush(stdout);
warned_about_zero_alpha = TRUE;
}
if (extract_data.group->format ==
_bitmap_group_format_compressed_color_key_transparency)
{
if ((color >> 24) != 0 && (color >> 24) != 0xFF && !warned_about_dxt1_alpha)
{
fprintf(
stdout,
"==> !!WARNING!! bitmap with greater than 1-bit alpha being compressed as DXT1 <==\r\n");
fflush(stdout);
warned_about_dxt1_alpha = TRUE;
}
if (!(color >> 24) &&
((color & 0xFFFFFF) == extract_data.adjusted_bounds_reference ||
!extract_data.extract_sequences))
{
color = 0;
}
else
{
color |= 0xFF000000;
}
}
if ((extract_data.group->format ==
_bitmap_group_format_compressed_color_key_transparency ||
extract_data.group->format ==
_bitmap_group_format_compressed_explicit_alpha ||
extract_data.group->format ==
_bitmap_group_format_compressed_interpolated_alpha) &&
extract_data.group->type == _bitmap_group_type_interface_bitmaps)
{
error(
_error_immediate,
"### ERROR interface/linear bitmap cannot be DXT-compressed");
}
destination[source_x - adjusted_bounds.x0] = color;
}
}
if (extract_data.group->type == _bitmap_group_type_2d_textures ||
extract_data.group->type == _bitmap_group_type_interface_bitmaps)
{
short bitmap_index = extract_add_bitmap(bitmap);
if (bitmap_index != NONE)
{
if (extract_data.sequence->first_bitmap_index == NONE)
{
extract_data.sequence->first_bitmap_index = bitmap_index;
extract_data.sequence->bitmap_count = 0;
}
extract_data.sequence->bitmap_count++;
}
bitmap_delete(bitmap);
}
else if (!extract_data.extract_sequences)
{
if (extract_data.group->type == _bitmap_group_type_cube_maps)
{
extract_plateless_cube_map(bitmap);
}
else
{
error(
_error_silent,
"### ERROR extract: tried to extract non-2d textures without a valid place but they weren't cube maps and/or EXTRACT_PLATELESS_CUBE_MAPS aren't allowed");
result = FALSE;
}
bitmap_delete(bitmap);
}
else if (extract_data.bitmap_count < 0x400)
{
struct bitmap_extract_entry *entry =
&extract_data.bitmaps[extract_data.bitmap_count++];
entry->bitmap = bitmap;
entry->sequence_index = extract_data.sequence_index;
entry->sprite_index = NONE;
entry->page_index = NONE;
entry->page_entry_index = NONE;
if (extract_data.group->type == _bitmap_group_type_sprites)
{
short sprite_index =
(short)tag_block_add_element(&extract_data.sequence->sprites);
if (sprite_index != NONE)
{
struct bitmap_group_sprite *sprite = TAG_BLOCK_GET_ELEMENT(
&extract_data.sequence->sprites,
sprite_index,
struct bitmap_group_sprite);
sprite->bitmap_index = NONE;
if (TEST_FLAG(
extract_data.group->flags,
_bitmap_group_extract_sprites_filthy_bug_fix_bit))
{
sprite->registration_point.x =
(real)bitmap->registration_point.x * 0.5f;
sprite->registration_point.y =
(real)bitmap->registration_point.y * 0.5f;
}
else
{
sprite->registration_point.x = (real)bitmap->registration_point.x;
sprite->registration_point.y = (real)bitmap->registration_point.y;
}
entry->sprite_index = sprite_index;
}
else
{
error(_error_silent, "### ERROR extract: failed to add sprite to sequence");
result = FALSE;
}
}
}
else
{
error(
_error_silent,
"### ERROR extract: can't handle more than (#%d) temporary bitmaps",
0x400);
result = FALSE;
}
}
else
{
error(_error_silent, "### ERROR extract: failed to allocate temporary bitmap");
result = FALSE;
}
}
else
{
fprintf(stdout, "### WARNING skipped a bitmap which contained no data\r\n");
fflush(stdout);
}
return result;
}
static boolean extract_3d_textures(
void)
{
boolean result = TRUE;
short first_bitmap_index = 0;
while (result && first_bitmap_index < extract_data.bitmap_count)
{
struct bitmap_extract_entry *first_entry = &extract_data.bitmaps[first_bitmap_index];
short sequence_index = first_entry->sequence_index;
short width = first_entry->bitmap->width;
short height = first_entry->bitmap->height;
short bitmap_count = 0;
boolean incompatible_dimensions = FALSE;
// BUG (preserved): January does not bound the final run before reading its next sequence index.
while (!incompatible_dimensions &&
extract_data.bitmaps[first_bitmap_index + bitmap_count].sequence_index == sequence_index)
{
struct bitmap_data *bitmap =
extract_data.bitmaps[first_bitmap_index + bitmap_count].bitmap;
if (bitmap->width != width || bitmap->height != height)
incompatible_dimensions = TRUE;
bitmap_count++;
}
if (incompatible_dimensions)
{
fprintf(stdout, "skipping 3D texture with incompatible slices\r\n");
fflush(stdout);
}
else if (bitmap_count & (bitmap_count - 1))
{
fprintf(stdout, "skipping 3D texture with non power-of-two slice count\r\n");
fflush(stdout);
}
else
{
struct bitmap_data *bitmap =
bitmap_3d_new(width, height, bitmap_count, 0, _bitmap_format_a8r8g8b8);
if (bitmap && bitmap->base_address)
{
short slice_index;
short bitmap_index;
for (slice_index = 0; slice_index < bitmap_count; slice_index++)
{
bitmap_3d_slice_insert(
extract_data.bitmaps[first_bitmap_index + slice_index].bitmap,
bitmap,
0,
slice_index);
}
extract_data.sequence_index = sequence_index;
bitmap_index = extract_add_bitmap(bitmap);
if (bitmap_index != NONE)
{
struct bitmap_group_sequence *sequence = TAG_BLOCK_GET_ELEMENT(
&extract_data.group->sequences,
sequence_index,
struct bitmap_group_sequence);
if (sequence->first_bitmap_index == NONE)
{
sequence->first_bitmap_index = bitmap_index;
sequence->bitmap_count = 1;
}
else
{
sequence->bitmap_count++;
}
}
}
else
{
error(_error_silent, "### ERROR extract: failed to allocate temporary bitmap");
result = FALSE;
}
bitmap_delete(bitmap);
}
first_bitmap_index += bitmap_count;
}
return result;
}
static boolean extract_cube_maps(
void)
{
boolean result = TRUE;
struct bitmap_data *temporary_bitmap = NULL;
short face_index = 0;
short sequence_index;
short bitmap_index;
for (bitmap_index = 0; result && bitmap_index < extract_data.bitmap_count; bitmap_index++)
{
struct bitmap_extract_entry *entry = &extract_data.bitmaps[bitmap_index];
boolean skip_cube_map = FALSE;
if (face_index == 0)
{
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x798,
!temporary_bitmap);
if (entry->bitmap->width == entry->bitmap->height)
{
temporary_bitmap = bitmap_cube_map_new(
entry->bitmap->width,
0,
_bitmap_format_a8r8g8b8);
sequence_index = entry->sequence_index;
}
else
{
fprintf(stdout, "skipping cube map with non-square faces\r\n");
fflush(stdout);
skip_cube_map = TRUE;
}
}
if (temporary_bitmap && temporary_bitmap->base_address)
{
if (entry->sequence_index == sequence_index)
{
if (entry->bitmap->width == temporary_bitmap->width &&
entry->bitmap->height == temporary_bitmap->height)
{
bitmap_cube_map_face_insert(
entry->bitmap,
temporary_bitmap,
0,
face_index);
face_index++;
}
else
{
fprintf(stdout, "skipping cube map with incompatible-size faces\r\n");
fflush(stdout);
skip_cube_map = TRUE;
}
}
else
{
fprintf(stdout, "skipping cube map which spanned sequence\r\n");
fflush(stdout);
skip_cube_map = TRUE;
}
if (skip_cube_map)
{
/* BUG (original): January frees the partial cube map without clearing temporary_bitmap,
so the !temporary_bitmap assert fires at the next face-0 entry. */
bitmap_delete(temporary_bitmap);
face_index = 0;
}
}
else if (!skip_cube_map)
{
error(_error_silent, "### ERROR extract: failed to create temporary bitmap");
result = FALSE;
}
if (result && face_index == 6)
{
short group_bitmap_index = extract_add_bitmap(temporary_bitmap);
if (group_bitmap_index != NONE)
{
struct bitmap_group_sequence *sequence = TAG_BLOCK_GET_ELEMENT(
&extract_data.group->sequences,
sequence_index,
struct bitmap_group_sequence);
if (sequence->first_bitmap_index == NONE)
{
sequence->first_bitmap_index = group_bitmap_index;
sequence->bitmap_count = 0;
}
sequence->bitmap_count++;
}
bitmap_delete(temporary_bitmap);
temporary_bitmap = NULL;
face_index = 0;
}
bitmap_delete(entry->bitmap);
}
if (temporary_bitmap)
{
if (result)
{
fprintf(stdout, "skipping cube map with less than six faces\r\n");
fflush(stdout);
}
bitmap_delete(temporary_bitmap);
}
return result;
}
static boolean extract_sprites(
void)
{
boolean result = TRUE;
long total_page_pixel_count = 0;
short sprite_page_count = extract_data.group->sprite_budget_count;
short spacing = extract_data.group->mipmap_count == 1 ? 1 : 4;
long page_size = 32 << (sprite_page_count
? extract_data.group->sprite_budget_size
: _bitmap_group_sprite_budget_512);
long budget_pixel_count = sprite_page_count * page_size * page_size;
pixel32 background_colors[3] =
{
0x00000000,
0xFFFFFFFF,
0x7F7F7F7F,
};
short maximum_bitmap_dimension = (short)(page_size - 2 * spacing);
struct texture_page *texture_pages[32];
short texture_page_count;
short bitmap_index;
short page_index;
for (bitmap_index = 0; result && bitmap_index < extract_data.bitmap_count; bitmap_index++)
{
struct bitmap_extract_entry *entry = &extract_data.bitmaps[bitmap_index];
if (entry->bitmap &&
(entry->bitmap->width > maximum_bitmap_dimension ||
entry->bitmap->height > maximum_bitmap_dimension))
{
error(
_error_immediate,
"### ERROR one or more sprites do not fit in the requested page size");
result = FALSE;
}
}
if (result)
{
if (TEST_FLAG(
extract_data.group->flags,
_bitmap_group_uniform_sprite_sequences_bit))
{
error(
_error_immediate,
"### ERROR hey - don't even try it! (uniform sprite sequences)\n"
"don't fucking swim in that septic tank with your mouth open like that");
}
extract_build_texture_pages_by_sequence(
texture_pages,
&texture_page_count,
(short)page_size,
spacing);
extract_data.group->sprite_spacing = spacing;
}
for (page_index = 0; result && page_index < texture_page_count; page_index++)
{
struct texture_page *texture_page = texture_pages[page_index];
struct bitmap_data *page_bitmap = bitmap_2d_new(
texture_page->width,
texture_page->height,
0,
_bitmap_format_a8r8g8b8);
if (page_bitmap && page_bitmap->base_address)
{
short entry_index;
bitmap_fill(
page_bitmap,
background_colors[extract_data.group->sprite_usage]);
for (entry_index = 0;
result && entry_index < extract_data.bitmap_count;
entry_index++)
{
struct bitmap_extract_entry *entry = &extract_data.bitmaps[entry_index];
struct bitmap_group_sequence *sequence = TAG_BLOCK_GET_ELEMENT(
&extract_data.group->sequences,
entry->sequence_index,
struct bitmap_group_sequence);
struct bitmap_group_sprite *sprite = TAG_BLOCK_GET_ELEMENT(
&sequence->sprites,
entry->sprite_index,
struct bitmap_group_sprite);
if (entry->page_index == page_index)
{
struct texture_page_texture *texture = texture_page_texture_get(
texture_page,
entry->page_entry_index);
short sprite_spacing = spacing;
point2d destination_point;
if (!TEST_FLAG(
extract_data.group->flags,
_bitmap_group_extract_sprites_filthy_bug_fix_bit) &&
texture_page->textures->actual_count == 1)
{
sprite_spacing = 0;
}
sprite->bitmap_index = page_index;
sprite->registration_point.x =
(sprite_spacing + sprite->registration_point.x) / texture_page->width;
sprite->registration_point.y =
(sprite_spacing + sprite->registration_point.y) / texture_page->height;
sprite->bounds.x0 =
(real)(texture->x - sprite_spacing) / texture_page->width;
sprite->bounds.y0 =
(real)(texture->y - sprite_spacing) / texture_page->height;
sprite->bounds.x1 =
(real)(texture->x + texture->width + sprite_spacing) / texture_page->width;
sprite->bounds.y1 =
(real)(texture->y + texture->height + sprite_spacing) / texture_page->height;
if (sequence->first_bitmap_index == NONE)
{
sequence->first_bitmap_index = page_index;
sequence->bitmap_count = 1;
}
else
{
sequence->bitmap_count = page_index - sequence->first_bitmap_index;
}
destination_point.x = texture->x;
destination_point.y = texture->y;
bitmap_copy(
page_bitmap,
&destination_point,
NULL,
entry->bitmap,
NULL,
0xFFFFFFFF,
0);
bitmap_delete(entry->bitmap);
}
}
if (extract_add_bitmap(page_bitmap) != NONE)
{
total_page_pixel_count += texture_page->width * texture_page->height;
bitmap_delete(page_bitmap);
}
}
else
{
error(
_error_silent,
"### ERROR extract_sprite: failed to allocate texture page bitmap");
result = FALSE;
}
fprintf(
stdout,
"texture page created #%dx#%d (%3.2f%% used)\r\n",
texture_page->width,
texture_page->height,
texture_page_fraction_used(texture_page, TRUE) * 100.0f);
fflush(stdout);
texture_page_delete(texture_page);
}
if (result)
{
if ((real)budget_pixel_count == 0.0f)
{
fprintf(stdout, "### WARNING no sprite budget set\r\n");
fflush(stdout);
}
else
{
real budget_fraction =
(real)total_page_pixel_count / (real)budget_pixel_count;
if (budget_fraction <= 1.0f)
{
fprintf(stdout, "sprite budget met (%3.0f%%)\r\n", budget_fraction * 100.0f);
fflush(stdout);
}
else
{
error(
_error_silent,
"### ERROR sprite budget exceeded (%3.0f%%)",
budget_fraction * 100.0f);
result = FALSE;
}
}
}
return result;
}
static boolean extract_without_sequences(
void)
{
rectangle2d bounds;
struct bitmap_group *group = extract_data.group;
boolean result = TRUE;
if (group->format == _bitmap_group_format_compressed_color_key_transparency &&
extract_data.extract_sequences)
{
error(
_error_silent,
"### ERROR extract: compressed color-key transparency format must use a valid plate");
result = FALSE;
}
else
{
switch (group->type)
{
case _bitmap_group_type_2d_textures:
case _bitmap_group_type_cube_maps:
case _bitmap_group_type_interface_bitmaps:
{
short plate_width;
short plate_height;
short sequence_index;
bounds.y0 = 0;
bounds.x0 = 0;
plate_width = extract_data.plate->width;
plate_height = extract_data.plate->height;
bounds.x1 = plate_width;
bounds.y1 = plate_height;
sequence_index = (short)tag_block_add_element(&group->sequences);
extract_data.sequence_index = sequence_index;
extract_data.sequence = tag_block_get_element_with_size(
&extract_data.group->sequences,
sequence_index,
sizeof(struct bitmap_group_sequence));
extract_data.sequence->first_bitmap_index = NONE;
extract_bitmap(&bounds);
break;
}
case _bitmap_group_type_3d_textures:
error(_error_silent, "### ERROR can't extract 3D textures without a valid plate");
result = FALSE;
break;
case _bitmap_group_type_sprites:
error(_error_silent, "### ERROR can't extract sprites without a valid plate");
result = FALSE;
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x1A6,
FALSE,
"### ERROR unsupported bitmap group type");
break;
}
}
return result;
}
static boolean extract_sequence(
short top,
short bottom)
{
boolean result = TRUE;
short x;
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x234,
top>=0);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x235,
bottom>=top);
match_assert(
"c:\\halo\\SOURCE\\bitmaps\\bitmap_extract.c",
0x236,
bottom<=extract_data.plate->height);
x = 0;
while (result && x < extract_data.plate->width)
{
rectangle2d bounds;
short extraction_state = 0;
bounds.x0 = SHORT_MAX;
bounds.y0 = top;
bounds.x1 = SHORT_MIN;
bounds.y1 = bottom;
while (x < extract_data.plate->width && extraction_state != 2)
{
boolean found_bitmap = FALSE;
boolean found_bottom_reference = FALSE;
short y;
for (y = top; y < bottom; y++)
{
pixel32 color = *(pixel32 *)bitmap_2d_address(
extract_data.plate,
x,
y,
0) & 0xFFFFFF;
if (color == extract_data.bottom_reference)
{
found_bottom_reference = TRUE;
}
else if (color != extract_data.top_reference)
{
found_bitmap = TRUE;
switch (extraction_state)
{
case 0:
extraction_state = 1;
bounds.x0 = x;
/* fall through */
case 1:
bounds.y0 = MIN(y, bounds.y0);
bounds.y1 = MAX(y, bounds.y1);
bounds.x1 = x;
break;
}
}
}
if ((found_bottom_reference || extract_data.single_sequence) && !found_bitmap)
{
if (extraction_state == 1)
extraction_state = 2;
}
if (extraction_state == 1 && !found_bitmap)
{
extraction_state = 2;
}
x++;
}
if (extraction_state != 0)
{
rectangle2d adjusted_bounds;
bounds.x1++;
bounds.y1++;
adjusted_bounds = bounds;
if (TEST_FLAG(
extract_data.group->flags,
_bitmap_group_extract_sprites_filthy_bug_fix_bit))
{
short trim_y;
for (trim_y = adjusted_bounds.y0; trim_y < adjusted_bounds.y1; trim_y++)
{
short trim_x;
for (trim_x = adjusted_bounds.x0; trim_x < adjusted_bounds.x1; trim_x++)
{
pixel32 color = *(pixel32 *)bitmap_2d_address(
extract_data.plate,
trim_x,
trim_y,
0) & 0xFFFFFF;
if (color != extract_data.top_reference)
break;
}
if (trim_x < adjusted_bounds.x1)
break;
}
adjusted_bounds.y0 = trim_y;
for (trim_y = adjusted_bounds.y1 - 2; trim_y >= adjusted_bounds.y0; trim_y--)
{
short trim_x;
for (trim_x = adjusted_bounds.x0; trim_x < adjusted_bounds.x1; trim_x++)
{
pixel32 color = *(pixel32 *)bitmap_2d_address(
extract_data.plate,
trim_x,
trim_y,
0) & 0xFFFFFF;
if (color != extract_data.top_reference)
break;
}
if (trim_x < adjusted_bounds.x1)
break;
}
adjusted_bounds.y1 = trim_y + 1;
}
result = extract_bitmap(&adjusted_bounds);
}
}
return result;
}
static boolean extract_sequences(
void)
{
boolean result = TRUE;
short top = 1;
while (result && top < extract_data.plate->height)
{
short bottom;
short sequence_index;
bottom = extract_find_sequence_bounds(&top);
extract_warn_about_horizontal_border(bottom);
sequence_index = (short)tag_block_add_element(&extract_data.group->sequences);
if (sequence_index == NONE)
{
error(_error_silent, "### ERROR extract: failed to allocate sequence");
return FALSE;
}
extract_data.sequence_index = sequence_index;
extract_data.sequence = tag_block_get_element_with_size(
&extract_data.group->sequences,
sequence_index,
sizeof(struct bitmap_group_sequence));
extract_data.sequence->first_bitmap_index = NONE;
extract_data.sequence->bitmap_count = 0;
result = extract_sequence(top, bottom);
top = bottom + 1;
}
return result;
}