int hap_get_section_at_index(const void *input_buffer, uint32_t input_buffer_bytes, unsigned int index, const void **section, uint32_t *section_length, unsigned int *section_type) { int result; uint32_t section_header_length; result = hap_read_section_header(input_buffer, input_buffer_bytes, §ion_header_length, section_length, section_type); if (result != HapResult_No_Error) { return result; } if (*section_type == kHapSectionMultipleImages) { /* Step through until we find the section at index */ size_t offset = 0; size_t top_section_length = *section_length; input_buffer = ((uint8_t *)input_buffer) + section_header_length; section_header_length = 0; *section_length = 0; for (int i = 0; i <= index; i++) { offset += section_header_length + *section_length; if (offset >= top_section_length) { return HapResult_Bad_Arguments; } result = hap_read_section_header(((uint8_t *)input_buffer) + offset, top_section_length - offset, §ion_header_length, section_length, section_type); if (result != HapResult_No_Error) { return result; } } offset += section_header_length; *section = ((uint8_t *)input_buffer) + offset; return HapResult_No_Error; } else if (index == 0) { /* A single-texture frame with the texture as the top section. */ *section = ((uint8_t *)input_buffer) + section_header_length; return HapResult_No_Error; } else { *section = NULL; *section_length = 0; *section_type = 0; return HapResult_Bad_Arguments; } }
unsigned int HapGetFrameTextureFormat(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int *outputBufferTextureFormat) { unsigned int result = HapResult_No_Error; uint32_t sectionHeaderLength; uint32_t sectionLength; unsigned int sectionType; /* Check arguments */ if (inputBuffer == NULL || outputBufferTextureFormat == NULL ) { return HapResult_Bad_Arguments; } /* Read the frame's top-level section */ result = hap_read_section_header(inputBuffer, (uint32_t)inputBufferBytes, §ionHeaderLength, §ionLength, §ionType); if (result == HapResult_No_Error) { /* Pass the API enum value to match the constant out */ *outputBufferTextureFormat = hap_texture_format_constant_for_format_identifier(hap_bottom_4_bits(sectionType)); /* Check a valid format was present */ if (*outputBufferTextureFormat == 0) { result = HapResult_Bad_Frame; } } return result; }
unsigned int HapGetFrameTextureCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int *outputTextureCount) { int result; uint32_t section_header_length; uint32_t section_length; unsigned int section_type; result = hap_read_section_header(inputBuffer, inputBufferBytes, §ion_header_length, §ion_length, §ion_type); if (result != HapResult_No_Error) { return result; } if (section_type == kHapSectionMultipleImages) { /* Step through, counting sections */ uint32_t offset = section_header_length; uint32_t top_section_length = section_length; *outputTextureCount = 0; while (offset < top_section_length) { result = hap_read_section_header(((uint8_t *)inputBuffer) + offset, inputBufferBytes - offset, §ion_header_length, §ion_length, §ion_type); if (result != HapResult_No_Error) { return result; } offset += section_header_length + section_length; *outputTextureCount += 1; } return HapResult_No_Error; } else { /* A single-texture frame with the texture as the top section. */ *outputTextureCount = 1; return HapResult_No_Error; } }
unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes, HapDecodeCallback callback, void *info, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed, unsigned int *outputBufferTextureFormat) { int result = HapResult_No_Error; uint32_t sectionHeaderLength; uint32_t sectionLength; unsigned int sectionType; unsigned int textureFormat; unsigned int compressor; const void *sectionStart; size_t bytesUsed = 0; /* Check arguments */ if (inputBuffer == NULL || outputBuffer == NULL || outputBufferTextureFormat == NULL ) { return HapResult_Bad_Arguments; } /* One top-level section type describes texture-format and second-stage compression */ result = hap_read_section_header(inputBuffer, (uint32_t)inputBufferBytes, §ionHeaderLength, §ionLength, §ionType); if (result != HapResult_No_Error) { return result; } /* Hap compressor/format constants can be unpacked by reading the top and bottom four bits. */ compressor = hap_top_4_bits(sectionType); textureFormat = hap_bottom_4_bits(sectionType); if (compressor == kHapCompressorComplex && callback == NULL) { return HapResult_Bad_Arguments; } /* Pass the texture format out */ *outputBufferTextureFormat = hap_texture_format_constant_for_format_identifier(textureFormat); if (*outputBufferTextureFormat == 0) { return HapResult_Bad_Frame; } sectionStart = ((uint8_t *)inputBuffer) + sectionHeaderLength; if (compressor == kHapCompressorComplex) { /* The top-level section should contain a Decode Instructions Container followed by frame data */ const char *frame_data = NULL; size_t bytes_remaining = 0; int chunk_count = 0; const void *compressors = NULL; const void *chunk_sizes = NULL; const void *chunk_offsets = NULL; result = hap_read_section_header(sectionStart, inputBufferBytes - sectionHeaderLength, §ionHeaderLength, §ionLength, §ionType); if (result == HapResult_No_Error && sectionType != kHapSectionDecodeInstructionsContainer) { result = HapResult_Bad_Frame; } if (result != HapResult_No_Error) { return result; } /* Frame data follows immediately after the Decode Instructions Container */ frame_data = ((const char *)sectionStart) + sectionHeaderLength + sectionLength; /* Step through the sections inside the Decode Instructions Container */ sectionStart = ((uint8_t *)sectionStart) + sectionHeaderLength; bytes_remaining = sectionLength; while (bytes_remaining > 0) { unsigned int section_chunk_count = 0; result = hap_read_section_header(sectionStart, bytes_remaining, §ionHeaderLength, §ionLength, §ionType); if (result != HapResult_No_Error) { return result; } sectionStart = ((uint8_t *)sectionStart) + sectionHeaderLength; switch (sectionType) { case kHapSectionChunkSecondStageCompressorTable: compressors = sectionStart; section_chunk_count = sectionLength; break; case kHapSectionChunkSizeTable: chunk_sizes = sectionStart; section_chunk_count = sectionLength / 4; break; case kHapSectionChunkOffsetTable: chunk_offsets = sectionStart; section_chunk_count = sectionLength / 4; break; default: // Ignore unrecognized sections break; } /* If we calculated a chunk count and already have one, make sure they match */ if (section_chunk_count != 0) { if (chunk_count != 0 && section_chunk_count != chunk_count) { return HapResult_Bad_Frame; } chunk_count = section_chunk_count; } sectionStart = ((uint8_t *)sectionStart) + sectionLength; bytes_remaining -= sectionHeaderLength + sectionLength; } /* The Chunk Second-Stage Compressor Table and Chunk Size Table are required */ if (compressors == NULL || chunk_sizes == NULL) { return HapResult_Bad_Frame; } if (chunk_count > 0) { /* Step through the chunks, storing information for their decompression */ HapChunkDecodeInfo *chunk_info = (HapChunkDecodeInfo *)malloc(sizeof(HapChunkDecodeInfo) * chunk_count); size_t running_compressed_chunk_size = 0; size_t running_uncompressed_chunk_size = 0; int i; if (chunk_info == NULL) { return HapResult_Internal_Error; } for (i = 0; i < chunk_count; i++) { chunk_info[i].compressor = *(((uint8_t *)compressors) + i); chunk_info[i].compressed_chunk_size = hap_read_4_byte_uint(((uint8_t *)chunk_sizes) + (i * 4)); if (chunk_offsets) { chunk_info[i].compressed_chunk_data = frame_data + hap_read_4_byte_uint(((uint8_t *)chunk_offsets) + (i * 4)); } else { chunk_info[i].compressed_chunk_data = frame_data + running_compressed_chunk_size; } running_compressed_chunk_size += chunk_info[i].compressed_chunk_size; if (chunk_info[i].compressor == kHapCompressorSnappy) { snappy_status snappy_result = snappy_uncompressed_length(chunk_info[i].compressed_chunk_data, chunk_info[i].compressed_chunk_size, &(chunk_info[i].uncompressed_chunk_size)); if (snappy_result != SNAPPY_OK) { switch (snappy_result) { case SNAPPY_INVALID_INPUT: result = HapResult_Bad_Frame; break; default: result = HapResult_Internal_Error; break; } break; } } else { chunk_info[i].uncompressed_chunk_size = chunk_info[i].compressed_chunk_size; } chunk_info[i].uncompressed_chunk_data = (char *)(((uint8_t *)outputBuffer) + running_uncompressed_chunk_size); running_uncompressed_chunk_size += chunk_info[i].uncompressed_chunk_size; } if (result == HapResult_No_Error && running_uncompressed_chunk_size > outputBufferBytes) { result = HapResult_Buffer_Too_Small; } if (result == HapResult_No_Error) { /* Perform decompression */ bytesUsed = running_uncompressed_chunk_size; callback((HapDecodeWorkFunction)hap_decode_chunk, chunk_info, chunk_count, info); /* Check to see if we encountered any errors and report one of them */ for (i = 0; i < chunk_count; i++) { if (chunk_info[i].result != HapResult_No_Error) { result = chunk_info[i].result; break; } } } free(chunk_info); if (result != HapResult_No_Error) { return result; } } } else if (compressor == kHapCompressorSnappy) { /* Only one section is present containing a single block of snappy-compressed S3 data */ snappy_status snappy_result = snappy_uncompressed_length((const char *)sectionStart, sectionLength, &bytesUsed); if (snappy_result != SNAPPY_OK) { return HapResult_Internal_Error; } if (bytesUsed > outputBufferBytes) { return HapResult_Buffer_Too_Small; } snappy_result = snappy_uncompress((const char *)sectionStart, sectionLength, (char *)outputBuffer, &bytesUsed); if (snappy_result != SNAPPY_OK) { return HapResult_Internal_Error; } } else if (compressor == kHapCompressorNone) { /* Only one section is present containing a single block of uncompressed S3 data */ bytesUsed = sectionLength; if (sectionLength > outputBufferBytes) { return HapResult_Buffer_Too_Small; } memcpy(outputBuffer, sectionStart, sectionLength); } else { return HapResult_Bad_Frame; } /* Fill out the remaining return value */ if (outputBufferBytesUsed != NULL) { *outputBufferBytesUsed = bytesUsed; } return HapResult_No_Error; }
static unsigned int hap_decode_header_complex_instructions(const void *texture_section, uint32_t texture_section_length, int * chunk_count, const void **compressors, const void **chunk_sizes, const void **chunk_offsets, const char **frame_data){ int result = HapResult_No_Error; const void *section_start; uint32_t section_header_length; uint32_t section_length; unsigned int section_type; size_t bytes_remaining = 0; *compressors = NULL; *chunk_sizes = NULL; *chunk_offsets = NULL; result = hap_read_section_header(texture_section, texture_section_length, §ion_header_length, §ion_length, §ion_type); if (result == HapResult_No_Error && section_type != kHapSectionDecodeInstructionsContainer) { result = HapResult_Bad_Frame; } if (result != HapResult_No_Error) { return result; } /* Frame data follows immediately after the Decode Instructions Container */ *frame_data = ((const char *)texture_section) + section_header_length + section_length; /* Step through the sections inside the Decode Instructions Container */ section_start = ((uint8_t *)texture_section) + section_header_length; bytes_remaining = section_length; while (bytes_remaining > 0) { unsigned int section_chunk_count = 0; result = hap_read_section_header(section_start, bytes_remaining, §ion_header_length, §ion_length, §ion_type); if (result != HapResult_No_Error) { return result; } section_start = ((uint8_t *)section_start) + section_header_length; switch (section_type) { case kHapSectionChunkSecondStageCompressorTable: *compressors = section_start; section_chunk_count = section_length; break; case kHapSectionChunkSizeTable: *chunk_sizes = section_start; section_chunk_count = section_length / 4; break; case kHapSectionChunkOffsetTable: *chunk_offsets = section_start; section_chunk_count = section_length / 4; break; default: // Ignore unrecognized sections break; } /* If we calculated a chunk count and already have one, make sure they match */ if (section_chunk_count != 0) { if ((*chunk_count) != 0 && section_chunk_count != (*chunk_count)) { return HapResult_Bad_Frame; } *chunk_count = section_chunk_count; } section_start = ((uint8_t *)section_start) + section_length; bytes_remaining -= section_header_length + section_length; } /* The Chunk Second-Stage Compressor Table and Chunk Size Table are required */ if (*compressors == NULL || *chunk_sizes == NULL) { return HapResult_Bad_Frame; } return result; }