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