def meta_to_tag_data(self, meta, tag_cls, tag_index_ref, **kwargs): magic = self.map_magic engine = self.engine map_data = self.map_data tag_index = self.tag_index is_xbox = get_is_xbox_map(engine) if tag_cls == "bitm": # set the size of the compressed plate data to nothing meta.compressed_color_plate_data.STEPTREE = BytearrayBuffer() new_pixels_offset = 0 # uncheck the prefer_low_detail flag and # set up the pixels_offset correctly. for bitmap in meta.bitmaps.STEPTREE: bitmap.flags.prefer_low_detail = is_xbox bitmap.pixels_offset = new_pixels_offset new_pixels_offset += bitmap.pixels_meta_size # clear some meta-only fields bitmap.pixels_meta_size = 0 bitmap.bitmap_id_unknown1 = bitmap.bitmap_id_unknown2 = 0 bitmap.bitmap_data_pointer = bitmap.base_address = 0 elif tag_cls == "snd!": meta.maximum_bend_per_second = meta.maximum_bend_per_second**30 for pitch_range in meta.pitch_ranges.STEPTREE: for permutation in pitch_range.permutations.STEPTREE: if permutation.compression.enum_name == "none": # byteswap pcm audio byteswap_pcm16_samples(permutation.samples) return meta
def extract_bitmaps(tagdata, tag_path, **kw): filepath_base = os.path.join( kw['out_dir'], os.path.splitext(tag_path)[0]) ext = kw.get("bitmap_ext", "").strip(". ") keep_alpha = kw.get("bitmap_keep_alpha", True) engine = kw.pop('engine', '') pix_data = tagdata.processed_pixel_data.STEPTREE if 'halo_map' in kw: engine = kw['halo_map'].engine p8_palette = STUBBS_P8_PALETTE if "stubbs" in engine else HALO_P8_PALETTE if not ext: ext = "dds" is_xbox = get_is_xbox_map(engine) is_gen3 = hasattr(tagdata, "zone_assets_normal") if Arbytmap is None: # cant extract xbox bitmaps yet return " Arbytmap not loaded. Cannot extract bitmaps." arby = Arbytmap() bitm_i = 0 multi_bitmap = len(tagdata.bitmaps.STEPTREE) > 1 size_calc = get_h3_pixel_bytes_size if is_gen3 else get_pixel_bytes_size dim_calc = get_virtual_dimension if is_gen3 else None for bitmap in tagdata.bitmaps.STEPTREE: typ = bitmap.type.enum_name fmt = bitmap.format.enum_name w = bitmap.width h = bitmap.height d = bitmap.depth tiled = False if hasattr(bitmap, "format_flags"): tiled = bitmap.format_flags.tiled filepath = filepath_base if multi_bitmap: filepath += "__%s" % bitm_i bitm_i += 1 tex_block = [] tex_info = dict( width=w, height=h, depth=d, mipmap_count=bitmap.mipmaps, swizzled=bitmap.flags.swizzled, big_endian=is_gen3, packed=True, tiled=tiled, tile_method="DXGI", packed_width_calc=dim_calc, packed_height_calc=dim_calc, filepath=filepath + "." + ext, ) tex_info["texture_type"] = { "texture_2d": TYPE_2D, "texture_3d": TYPE_3D, "cubemap": TYPE_CUBEMAP}.get(typ, TYPE_2D) tex_info["sub_bitmap_count"] = { "texture_2d": 1, "texture_3d": 1, "cubemap": 6, "multipage_2d": d}.get(typ, 1) if typ == "multipage_2d": tex_info.update(depth=1) d = 1 if fmt == "p8_bump": tex_info.update( palette=[p8_palette.p8_palette_32bit_packed]*(bitmap.mipmaps + 1), palette_packed=True, indexing_size=8, format=FORMAT_P8_BUMP) else: tex_info["format"] = { "a8": FORMAT_A8, "y8": FORMAT_L8, "ay8": FORMAT_AL8, "a8y8": FORMAT_A8L8, "p8": FORMAT_A8, "v8u8": FORMAT_V8U8, "g8b8": FORMAT_R8G8, "x8r8g8b8": FORMAT_A8R8G8B8, "a8r8g8b8": FORMAT_A8R8G8B8, "r5g6b5": FORMAT_R5G6B5, "a1r5g5b5": FORMAT_A1R5G5B5, "a4r4g4b4": FORMAT_A4R4G4B4, "dxt1": FORMAT_DXT1, "dxt3": FORMAT_DXT3, "dxt5": FORMAT_DXT5, "ctx1": FORMAT_CTX1, "dxn": FORMAT_DXN, "dxt5ay": FORMAT_DXT5AY, "dxt3a": FORMAT_DXT3A, "dxt3y": FORMAT_DXT3Y, "dxt5a": FORMAT_DXT5A, "dxt5y": FORMAT_DXT5Y, "rgbfp16": FORMAT_R16G16B16F, "argbfp32": FORMAT_A32R32G32B32F, "rgbfp32": FORMAT_R32G32B32F}.get(fmt, None) arby_fmt = tex_info["format"] if arby_fmt is None: continue i_max = tex_info["sub_bitmap_count"] if is_xbox else bitmap.mipmaps + 1 j_max = bitmap.mipmaps + 1 if is_xbox else tex_info['sub_bitmap_count'] off = bitmap.pixels_offset for i in range(i_max): if not is_xbox: mip_size = size_calc(arby_fmt, w, h, d, i, tiled) for j in range(j_max): if is_xbox: mip_size = size_calc(arby_fmt, w, h, d, j, tiled) if fmt == "p8_bump": tex_block.append( array('B', pix_data[off: off + (mip_size // 4)])) off += len(tex_block[-1]) else: off = bitmap_io.bitmap_bytes_to_array( pix_data, off, tex_block, arby_fmt, 1, 1, 1, mip_size) # skip the xbox alignment padding to get to the next texture if is_xbox and typ == "cubemap": off += ((CUBEMAP_PADDING - (off % CUBEMAP_PADDING)) % CUBEMAP_PADDING) if is_xbox and typ == "cubemap": template = tuple(tex_block) i = 0 for f in (0, 2, 1, 3, 4, 5): for m in range(bitmap.mipmaps + 1): tex_block[m*6 + f] = template[i] i += 1 if not tex_block: # nothing to extract continue arby.load_new_texture(texture_block=tex_block, texture_info=tex_info, tile_mode=False, swizzle_mode=False) arby.save_to_file(keep_alpha=keep_alpha)
def inject_rawdata(self, meta, tag_cls, tag_index_ref): bitmaps = self.maps.get("bitmaps") sounds = self.maps.get("sounds") loc = self.maps.get("loc") magic = self.map_magic engine = self.engine map_data = self.map_data try: bitmap_data = bitmaps.map_data except Exception: bitmap_data = None try: sound_data = sounds.map_data except Exception: sound_data = None try: loc_data = loc.map_data except Exception: loc_data = None is_not_indexed = not self.is_indexed(tag_index_ref.id & 0xFFff) might_be_in_rsrc = engine in ("halo1pc", "halo1pcdemo", "halo1ce", "halo1yelo", "halo1vap") might_be_in_rsrc &= not self.is_resource # get some rawdata that would be pretty annoying to do in the parser if tag_cls == "bitm": # grab bitmap data from map new_pixels = BytearrayBuffer() # to enable compatibility with my bitmap converter we'll set the # base address to a certain constant based on the console platform is_xbox = get_is_xbox_map(engine) for bitmap in meta.bitmaps.STEPTREE: pixel_data = map_data if might_be_in_rsrc and bitmap.flags.data_in_resource_map: pixel_data = bitmap_data if pixel_data is None: return # grab the bitmap data from this map(no magic used) pixel_data.seek(bitmap.pixels_offset) new_pixels += pixel_data.read(bitmap.pixels_meta_size) bitmap.base_address = 1073751810 * is_xbox meta.processed_pixel_data.STEPTREE = new_pixels elif tag_cls == "font": # might need to grab pixel data from resource map meta_offset = tag_index_ref.meta_offset if is_not_indexed: return meta elif not self.is_resource: if loc is None or loc.map_header is None: return meta_offset = loc.rsrc_map.data.tags[meta_offset].tag.offset if loc_data is None: return loc_data.seek(meta.pixels.pointer + meta_offset) meta.pixels.data = loc_data.read(meta.pixels.size) elif tag_cls == "hmt ": # might need to grab string data from resource map meta_offset = tag_index_ref.meta_offset if is_not_indexed: return meta elif not self.is_resource: if loc is None or loc.map_header is None: return meta_offset = loc.rsrc_map.data.tags[meta_offset].tag.offset b = meta.string loc_data.seek(b.pointer + meta_offset) meta.string.data = loc_data.read(b.size).decode('utf-16-le') elif tag_cls == "snd!": # might need to get samples and permutations from the resource map is_pc = engine in ("halo1pc", "halo1pcdemo") is_ce = engine in ("halo1ce", "halo1yelo", "halo1vap") if not (is_pc or is_ce): return meta elif sound_data is None: return # ce tagpaths are in the format: path__permutations # ex: sound\sfx\impulse\coolant\enter_water__permutations # # pc tagpaths are in the format: path__pitchrange__permutation # ex: sound\sfx\impulse\coolant\enter_water__0__0 other_data = map_data sound_magic = 0 - magic # DO NOT optimize this section. The logic is like this on purpose if is_pc: pass elif self.is_resource: other_data = sound_data sound_magic = tag_index_ref.meta_offset + meta.get_size() elif sounds is None: return for pitches in meta.pitch_ranges.STEPTREE: for perm in pitches.permutations.STEPTREE: for b in (perm.samples, perm.mouth_data, perm.subtitle_data): inject_sound_data(other_data, sound_data, b, sound_magic) elif tag_cls == "ustr": # might need to grab string data from resource map meta_offset = tag_index_ref.meta_offset if is_not_indexed: return meta elif not self.is_resource: if loc is None or loc.map_header is None: return meta_offset = loc.rsrc_map.data.tags[meta_offset].tag.offset string_blocks = meta.strings.STEPTREE if len(string_blocks): desc = string_blocks[0].get_desc('STEPTREE') parser = desc['TYPE'].parser try: FieldType.force_little() for b in string_blocks: parser(desc, None, b, 'STEPTREE', loc_data, meta_offset, b.pointer) FieldType.force_normal() except Exception: print(format_exc()) FieldType.force_normal() raise