f | import sys | f | import sys |
| | | |
n | def parse_file_header(data): | n | def extract_bmp_header(content): |
| if len(data) < 14: | | if len(content) < 14: |
| return (None, 'Invalid BMP file') | | return (None, 'Invalid BMP file') |
n | if data[:2] != b'BM': | n | if content[:2] != b'BM': |
| return (None, 'Not a Windows BMP') | | return (None, 'Not a Windows BMP') |
n | file_size = int.from_bytes(data[2:6], 'little') | n | bmp_size = int.from_bytes(content[2:6], 'little') |
| offset_to_pixels = int.from_bytes(data[10:14], 'little') | | pixel_offset = int.from_bytes(content[10:14], 'little') |
| return ((file_size, offset_to_pixels), None) | | return ((bmp_size, pixel_offset), None) |
| | | |
n | def check_file_size(data, declared_size): | n | def validate_bmp_size(content, expected_size): |
| if declared_size != len(data): | | return (True, None) if len(content) == expected_size else (False, 'I |
| | | ncorrect size') |
| return (False, 'Incorrect size') | | |
| return (True, None) | | |
| | | |
n | def parse_dib_header(data): | n | def extract_dib_info(content): |
| if len(data) < 18: | | if len(content) < 18: |
| return (None, 'Incomplete DIB header') | | return (None, 'Incomplete DIB header') |
n | dib_size = int.from_bytes(data[14:18], 'little') | n | header_length = int.from_bytes(content[14:18], 'little') |
| if dib_size not in {12, 40, 56, 108, 124}: | | if header_length not in {12, 40, 56, 108, 124}: |
| return (None, 'Incorrect header size') | | return (None, 'Incorrect header size') |
n | return (dib_size, None) | n | return (header_length, None) |
| | | |
n | def retrieve_image_info(data, dib_size): | n | def get_image_metadata(content, header_length): |
| if dib_size == 12: | | if header_length == 12: |
| if len(data) < 26: | | if len(content) < 26: |
| return (None, 'DIB header too short') | | return (None, 'DIB header too short') |
n | width = int.from_bytes(data[18:20], 'little') | n | w = int.from_bytes(content[18:20], 'little') |
| height = int.from_bytes(data[20:22], 'little', signed=True) | | h = int.from_bytes(content[20:22], 'little', signed=True) |
| planes = int.from_bytes(data[22:24], 'little') | | color_planes = int.from_bytes(content[22:24], 'little') |
| bit_depth = int.from_bytes(data[24:26], 'little') | | bit_depth = int.from_bytes(content[24:26], 'little') |
| compression_method = 0 | | compression = 0 |
| else: | | else: |
n | if len(data) < 34: | n | if len(content) < 34: |
| return (None, 'Incomplete DIB header') | | return (None, 'Incomplete DIB header') |
n | width = int.from_bytes(data[18:22], 'little', signed=True) | n | w = int.from_bytes(content[18:22], 'little', signed=True) |
| height = int.from_bytes(data[22:26], 'little', signed=True) | | h = int.from_bytes(content[22:26], 'little', signed=True) |
| planes = int.from_bytes(data[26:28], 'little') | | color_planes = int.from_bytes(content[26:28], 'little') |
| bit_depth = int.from_bytes(data[28:30], 'little') | | bit_depth = int.from_bytes(content[28:30], 'little') |
| compression_method = int.from_bytes(data[30:34], 'little') | | compression = int.from_bytes(content[30:34], 'little') |
| return ((width, height, bit_depth, compression_method), None) | | return ((w, h, bit_depth, compression), None) |
| | | |
n | def calculate_pixel_data_size(width, height, bit_depth): | n | def compute_pixel_storage(w, h, bit_depth): |
| height = abs(height) | | h = abs(h) |
| row_width = (width * bit_depth + 31) // 32 * 4 | | row_size = (w * bit_depth + 31) // 32 * 4 |
| return row_width * height | | return row_size * h |
| | | |
n | def verify_image_size(data, dib_size, expected_pixel_data_size): | n | def validate_image_size(content, header_length, computed_size): |
| if dib_size >= 40 and len(data) >= 38: | | if header_length >= 40 and len(content) >= 38: |
| declared_image_size = int.from_bytes(data[34:38], 'little') | | stated_size = int.from_bytes(content[34:38], 'little') |
| else: | | else: |
n | declared_image_size = 0 | n | stated_size = 0 |
| if declared_image_size == 0: | | if stated_size == 0: |
| declared_image_size = expected_pixel_data_size | | stated_size = computed_size |
| elif declared_image_size not in {expected_pixel_data_size, expected_ | | elif stated_size not in {computed_size, computed_size + 2}: |
| pixel_data_size + 2}: | | |
| return (False, 'Incorrect image size') | | return (False, 'Incorrect image size') |
n | extra_bytes = 2 if declared_image_size > expected_pixel_data_size el | n | pad = 2 if stated_size > computed_size else 0 |
| se 0 | | |
| return (extra_bytes, None) | | return (pad, None) |
| | | |
n | def analyze_bmp(data): | n | def parse_bmp(content): |
| try: | | try: |
n | header, error = parse_file_header(data) | n | header, err = extract_bmp_header(content) |
| if error: | | if err: |
| return error | | return err |
| file_size, pixel_data_offset = header | | bmp_size, pixel_offset = header |
| valid, error = check_file_size(data, file_size) | | is_valid, err = validate_bmp_size(content, bmp_size) |
| if not valid: | | if not is_valid: |
| return error | | return err |
| dib_size, error = parse_dib_header(data) | | dib_length, err = extract_dib_info(content) |
| if error: | | if err: |
| return error | | return err |
| image_data, error = retrieve_image_info(data, dib_size) | | metadata, err = get_image_metadata(content, dib_length) |
| if error: | | if err: |
| return error | | return err |
| width, height, bit_depth, compression = image_data | | w, h, bit_depth, compression = metadata |
| expected_pixel_data_size = calculate_pixel_data_size(width, heig | | required_size = compute_pixel_storage(w, h, bit_depth) |
| ht, bit_depth) | | |
| padding_bytes, error = verify_image_size(data, dib_size, expecte | | pad_size, err = validate_image_size(content, dib_length, require |
| d_pixel_data_size) | | d_size) |
| if error: | | if err: |
| return error | | return err |
| return f'{width} {abs(height)} {bit_depth} {compression} {paddin | | return f'{w} {abs(h)} {bit_depth} {compression} {pad_size}' |
| g_bytes}' | | |
| except Exception as exc: | | except Exception as ex: |
| return f'Unhandled error: {exc}' | | return f'Unhandled error: {ex}' |
| if __name__ == '__main__': | | if __name__ == '__main__': |
t | bmp_content = sys.stdin.buffer.read() | t | bmp_data = sys.stdin.buffer.read() |
| analysis_result = analyze_bmp(bmp_content) | | print(parse_bmp(bmp_data)) |
| print(analysis_result) | | |