Word Unperfect
public
Read
Owner: themaster
Branch: main
Commits: 0
Git CLI clone URL
git clone https://www.xt-emporium.com/git/word-unperfect.git
Fullscreen desktop URL
Code
Commits
History
Branches
Bug Reports
Discussions
Compare
Settings
word-unperfect
/
rev
/
wp_resource_manager.c
File editor
#include "wp_resource_manager.h" #include "wp_file_format.h" #include <stdio.h> #include <string.h> #define WP_KEYBOARD_BINDING_TABLE_OFFSET 0x40U #define WP_KEYBOARD_SECTION_DIRECTORY_OFFSET 0x1AU #define WP_KEYBOARD_SECTION_ROW_SIZE 10U #define WP_KEYBOARD_BINDING_SECTION_ID 1U #define WP_KEYBOARD_DESCRIPTOR_SECTION_ID 3U #define WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE 5U #define WP_MACRO_TITLE_LENGTH_OFFSET 0x1CU #define WP_MACRO_TITLE_POINTER_OFFSET 0x20U #define WP_PRINTER_NAME_POINTER_WORD_OFFSET 0x08U /* Minimal Unicode map for common WP extended characters. The table is kept * small for the host scaffold but is structured so the full WP character-set * table can be dropped in without changing callers. */ static const WpExtendedChar wp_extended_map[] = { {1, 0, 0x00C7}, /* C with cedilla */ {1, 1, 0x00FC}, /* u with diaeresis */ {1, 2, 0x00E9}, /* e with acute */ {1, 3, 0x00E2}, /* a with circumflex */ {4, 0, 0x00E1}, /* a with acute */ {4, 1, 0x00E0}, /* a with grave */ {4, 3, 0x00F6}, /* o with diaeresis */ {0, 0, 0} }; static uint16_t wp_res_le16(const uint8_t *p) { return (uint16_t)p[0] | ((uint16_t)p[1] << 8U); } static uint32_t wp_res_le32(const uint8_t *p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8U) | ((uint32_t)p[2] << 16U) | ((uint32_t)p[3] << 24U); } static bool wp_res_byte_bitmap_test(const uint8_t *bitmap, uint8_t value) { if (bitmap == NULL) { return false; } return (bitmap[(unsigned)value >> 3U] & (uint8_t)(1U << (value & 7U))) != 0U; } static void wp_res_byte_bitmap_add(uint8_t *bitmap, uint8_t value) { if (bitmap == NULL) { return; } bitmap[(unsigned)value >> 3U] |= (uint8_t)(1U << (value & 7U)); } static bool wp_res_find_prefix_section(const WpLoadedFile *file, uint16_t wanted_id, size_t *out_offset, size_t *out_size) { size_t row; if (file == NULL || file->prefix_bytes == NULL || out_offset == NULL || out_size == NULL) { return false; } for (row = WP_KEYBOARD_SECTION_DIRECTORY_OFFSET; row + WP_KEYBOARD_SECTION_ROW_SIZE <= file->prefix_size; row += WP_KEYBOARD_SECTION_ROW_SIZE) { uint16_t section_id = wp_res_le16(file->prefix_bytes + row); uint32_t section_size; uint16_t section_offset; if (section_id == 0U) { break; } section_size = wp_res_le32(file->prefix_bytes + row + 2U); section_offset = wp_res_le16(file->prefix_bytes + row + 6U); if (section_id == wanted_id && section_offset < file->prefix_size && section_size <= (uint32_t)(file->prefix_size - section_offset)) { *out_offset = section_offset; *out_size = (size_t)section_size; return true; } } return false; } static void wp_res_info_from_loaded(const WpLoadedFile *file, WpResourceFileInfo *out_info) { WpResourceFileInfo info; memset(&info, 0, sizeof(info)); memcpy(info.header.signature, file->header.signature, sizeof(info.header.signature)); info.header.data_offset = file->header.data_offset; info.header.product_type = file->header.product_type; info.header.file_type = file->header.file_type; info.header.major_version = file->header.major_version; info.header.minor_version = file->header.minor_version; info.file_size = file->header.file_size; info.prefix_size = (uint32_t)file->prefix_size; info.body_size = (uint32_t)file->logical_size; info.family = wp_res_family_for_type(file->header.file_type); info.has_body = file->logical_size != 0U; *out_info = info; } static bool wp_res_is_macro_title_char(uint8_t ch) { return ch >= 0x20U && ch < 0x7FU; } static bool wp_res_is_resource_text_char(uint8_t ch) { return ch >= 0x20U && ch < 0x7FU; } static void wp_res_copy_text(char *dest, size_t capacity, const uint8_t *src, size_t length) { size_t copy_len; if (dest == NULL || capacity == 0U || src == NULL) { return; } copy_len = length; if (copy_len + 1U > capacity) { copy_len = capacity - 1U; } { size_t i; for (i = 0U; i < copy_len; ++i) { uint8_t ch = src[i]; dest[i] = (ch == '"' || ch == '\\') ? '\'' : (char)ch; } } dest[copy_len] = '\0'; } static void wp_res_append_macro_preview_char(WpMacroStreamSummary *summary, uint8_t ch) { if (summary == NULL || summary->literal_preview_length + 1U >= WP_RES_MACRO_PREVIEW_CAPACITY) { return; } if (ch == '\t' || ch == '\n' || ch == '\r') { summary->literal_preview[summary->literal_preview_length++] = ' '; summary->literal_preview[summary->literal_preview_length] = '\0'; } else if (ch >= 0x20U && ch < 0x7FU) { summary->literal_preview[summary->literal_preview_length++] = (char)ch; summary->literal_preview[summary->literal_preview_length] = '\0'; } } static void wp_res_generic_consider_string(WpGenericResourceSummary *summary, const uint8_t *text, size_t length, bool length_prefixed) { if (summary == NULL || text == NULL || length == 0U) { return; } if (length_prefixed) { summary->length_prefixed_strings++; summary->length_prefixed_string_bytes += length; } if (!summary->has_string) { summary->has_string = true; wp_res_copy_text(summary->first_string, sizeof(summary->first_string), text, length); } if (length > summary->longest_string_length) { summary->longest_string_length = length; wp_res_copy_text(summary->longest_string, sizeof(summary->longest_string), text, length); } } static void wp_res_generic_scan_printable_runs(WpGenericResourceSummary *summary, const uint8_t *bytes, size_t size) { size_t offset = 0U; if (summary == NULL || bytes == NULL) { return; } while (offset < size) { size_t start; while (offset < size && !wp_res_is_resource_text_char(bytes[offset])) { offset++; } start = offset; while (offset < size && wp_res_is_resource_text_char(bytes[offset])) { offset++; } if (offset - start >= 4U) { summary->printable_runs++; summary->printable_bytes += offset - start; wp_res_generic_consider_string(summary, bytes + start, offset - start, false); } } } static bool wp_res_generic_length_string_candidate(const uint8_t *bytes, size_t size, size_t offset, size_t *out_length) { size_t declared; size_t length; size_t i; if (bytes == NULL || out_length == NULL || offset >= size) { return false; } declared = bytes[offset]; if (declared < 2U || declared + 1U > WP_RES_GENERIC_TEXT_CAPACITY || declared > size - offset - 1U) { return false; } length = declared; for (i = 0U; i < declared; ++i) { uint8_t ch = bytes[offset + 1U + i]; if (ch == 0U) { length = i; break; } if (!wp_res_is_resource_text_char(ch)) { return false; } } if (length < 2U) { return false; } *out_length = length; return true; } static void wp_res_generic_scan_length_strings(WpGenericResourceSummary *summary, const uint8_t *bytes, size_t size) { size_t offset; if (summary == NULL || bytes == NULL) { return; } for (offset = 0U; offset < size; ++offset) { size_t length; if (!wp_res_generic_length_string_candidate(bytes, size, offset, &length)) { continue; } wp_res_generic_consider_string(summary, bytes + offset + 1U, length, true); offset += length; } } static void wp_res_analyze_generic_loaded(const WpLoadedFile *file, WpGenericResourceSummary *out_summary) { WpGenericResourceSummary summary; size_t offset; memset(&summary, 0, sizeof(summary)); if (file == NULL) { *out_summary = summary; return; } summary.prefix_bytes = file->prefix_size; summary.body_bytes = file->logical_size; summary.body_words = file->logical_size / 2U; summary.odd_body_size = (file->logical_size & 1U) != 0U; if (file->logical_bytes != NULL) { for (offset = 0U; offset + 1U < file->logical_size; offset += 2U) { uint16_t word = wp_res_le16(file->logical_bytes + offset); if (word == 0U) { summary.zero_words++; } else if (word == 0xFFFFU) { summary.ffff_words++; } else if ((size_t)word < file->logical_size) { summary.offset_like_words++; if (word > summary.highest_offset_like_word) { summary.highest_offset_like_word = word; } } } wp_res_generic_scan_printable_runs(&summary, file->logical_bytes, file->logical_size); wp_res_generic_scan_length_strings(&summary, file->logical_bytes, file->logical_size); } if (file->prefix_bytes != NULL) { wp_res_generic_scan_printable_runs(&summary, file->prefix_bytes, file->prefix_size); wp_res_generic_scan_length_strings(&summary, file->prefix_bytes, file->prefix_size); } *out_summary = summary; } static void wp_res_extract_macro_title(const WpLoadedFile *file, WpMacroStreamSummary *summary) { uint32_t title_bytes; uint32_t title_offset; uint32_t i; bool started = false; if (file == NULL || summary == NULL || file->prefix_bytes == NULL || file->prefix_size < WP_MACRO_TITLE_POINTER_OFFSET + 4U) { return; } title_bytes = wp_res_le32(file->prefix_bytes + WP_MACRO_TITLE_LENGTH_OFFSET); title_offset = wp_res_le32(file->prefix_bytes + WP_MACRO_TITLE_POINTER_OFFSET); summary->title_offset = title_offset; summary->title_bytes = title_bytes; if (title_bytes == 0U || title_offset >= file->prefix_size || title_bytes > (uint32_t)(file->prefix_size - title_offset)) { return; } for (i = 0U; i < title_bytes; ++i) { uint8_t ch = file->prefix_bytes[title_offset + i]; if (!started && ch == 0U) { continue; } started = true; if (ch == 0U) { break; } if (!wp_res_is_macro_title_char(ch)) { continue; } if (summary->title_length + 1U >= WP_RES_MACRO_TITLE_CAPACITY) { break; } summary->title[summary->title_length++] = (char)ch; summary->title[summary->title_length] = '\0'; } summary->has_title = summary->title_length != 0U; } static void wp_res_finish_macro_command_stats(WpMacroStreamSummary *summary) { unsigned opcode; if (summary == NULL) { return; } for (opcode = 0U; opcode < WP_RES_MACRO_COMMAND_OPCODE_COUNT; ++opcode) { size_t count = summary->command_histogram[opcode]; if (count == 0U) { continue; } summary->unique_command_opcodes++; if (count > summary->most_common_command_count) { summary->most_common_command_count = count; summary->most_common_command_opcode = (uint8_t)opcode; } } } static void wp_res_finish_macro_dry_run_stats(WpMacroDryRunStats *stats) { unsigned opcode; if (stats == NULL) { return; } for (opcode = 0U; opcode < WP_RES_MACRO_COMMAND_OPCODE_COUNT; ++opcode) { if (stats->command_histogram[opcode] == 0U) { continue; } stats->unique_command_opcodes++; stats->unique_unsupported_command_opcodes++; } } static void wp_res_macro_dry_run_word(WpMacroDryRunStats *stats, uint16_t word) { if (stats == NULL) { return; } stats->words++; stats->events++; if (word == 0U) { stats->zero_events++; } else if ((word & 0xFF00U) == 0xFC00U) { uint8_t opcode = (uint8_t)(word & 0x00FFU); stats->command_events++; stats->unsupported_command_events++; stats->command_histogram[opcode]++; if (!stats->has_command) { stats->first_command_opcode = opcode; stats->has_command = true; } stats->last_command_opcode = opcode; } else if (word >= 0x8000U) { stats->extended_events++; } else if ((word >= 0x20U && word < 0x7FU) || word == '\t' || word == '\n' || word == '\r') { stats->literal_events++; } else { stats->control_events++; } } WpResourceFamily wp_res_family_for_type(uint8_t file_type) { switch (file_type) { case 0x01U: return WP_RES_FAMILY_MACRO; case 0x02U: case 0x24U: return WP_RES_FAMILY_VIDEO_RESOURCE; case 0x03U: return WP_RES_FAMILY_KEYBOARD; case 0x0AU: return WP_RES_FAMILY_DOCUMENT_STYLE; case 0x0BU: case 0x0CU: return WP_RES_FAMILY_SPELLER; case 0x10U: return WP_RES_FAMILY_PRINTER; case 0x15U: return WP_RES_FAMILY_OVERLAY_HELP; case 0x16U: return WP_RES_FAMILY_GRAPHIC; case 0x19U: return WP_RES_FAMILY_MACRO_RESOURCE; case 0x1EU: return WP_RES_FAMILY_QUICK_REFERENCE; case 0x2AU: return WP_RES_FAMILY_INDEX_RESOURCE; default: return WP_RES_FAMILY_UNKNOWN; } } const char *wp_res_family_name(WpResourceFamily family) { switch (family) { case WP_RES_FAMILY_MACRO: return "macro"; case WP_RES_FAMILY_VIDEO_RESOURCE: return "video/resource"; case WP_RES_FAMILY_KEYBOARD: return "keyboard"; case WP_RES_FAMILY_DOCUMENT_STYLE: return "document/style"; case WP_RES_FAMILY_SPELLER: return "speller"; case WP_RES_FAMILY_PRINTER: return "printer"; case WP_RES_FAMILY_OVERLAY_HELP: return "overlay/help"; case WP_RES_FAMILY_GRAPHIC: return "graphic"; case WP_RES_FAMILY_MACRO_RESOURCE: return "macro-resource"; case WP_RES_FAMILY_QUICK_REFERENCE: return "quick-reference"; case WP_RES_FAMILY_INDEX_RESOURCE: return "index/resource"; case WP_RES_FAMILY_UNKNOWN: case WP_RES_FAMILY_COUNT: default: return "unknown"; } } uint32_t wp_char_to_unicode(uint8_t set, uint8_t index) { const WpExtendedChar *m; if (set == 0) { return index; /* Standard single-byte host/CP437-compatible range. */ } for (m = wp_extended_map; m->unicode != 0; ++m) { if (m->set == set && m->index == index) { return m->unicode; } } return '?'; } bool wp_unicode_to_char(uint32_t unicode, uint8_t *out_set, uint8_t *out_index) { const WpExtendedChar *m; if (out_set == NULL || out_index == NULL) { return false; } if (unicode < 0x80U) { *out_set = 0U; *out_index = (uint8_t)unicode; return true; } for (m = wp_extended_map; m->unicode != 0; ++m) { if (m->unicode == unicode) { *out_set = m->set; *out_index = m->index; return true; } } return false; } bool wp_res_read_header(const char *filename, WpResourceHeader *out_header) { WpFileHeader common; if (filename == NULL || out_header == NULL) { return false; } if (!wp_file_read_header(filename, &common)) { return false; } memcpy(out_header->signature, common.signature, sizeof(out_header->signature)); out_header->data_offset = common.data_offset; out_header->product_type = common.product_type; out_header->file_type = common.file_type; out_header->major_version = common.major_version; out_header->minor_version = common.minor_version; return true; } bool wp_res_describe_file(const char *filename, WpResourceFileInfo *out_info) { WpLoadedFile file; if (filename == NULL || out_info == NULL) { return false; } if (!wp_file_load_body(filename, &file)) { return false; } wp_res_info_from_loaded(&file, out_info); wp_file_free(&file); return true; } bool wp_res_macro_dry_run_file(const char *filename, WpMacroDryRunStats *out_stats) { WpLoadedFile file; WpMacroDryRunStats stats; size_t offset; if (filename == NULL || out_stats == NULL) { return false; } if (!wp_file_load_body(filename, &file)) { return false; } memset(&stats, 0, sizeof(stats)); if (wp_res_family_for_type(file.header.file_type) != WP_RES_FAMILY_MACRO) { wp_file_free(&file); return false; } stats.odd_body_size = (file.logical_size & 1U) != 0U; for (offset = 0U; offset + 1U < file.logical_size; offset += 2U) { uint16_t word = wp_res_le16(file.logical_bytes + offset); wp_res_macro_dry_run_word(&stats, word); } wp_res_finish_macro_dry_run_stats(&stats); wp_file_free(&file); *out_stats = stats; return true; } static void wp_res_analyze_macro_loaded(const WpLoadedFile *file, WpMacroStreamSummary *out_summary) { WpMacroStreamSummary summary; size_t offset; memset(&summary, 0, sizeof(summary)); if (file == NULL || file->logical_bytes == NULL) { *out_summary = summary; return; } wp_res_extract_macro_title(file, &summary); summary.odd_body_size = (file->logical_size & 1U) != 0U; summary.words = file->logical_size / 2U; for (offset = 0U; offset + 1U < file->logical_size; offset += 2U) { uint16_t word = wp_res_le16(file->logical_bytes + offset); if (word == 0U) { summary.zero_words++; } else if ((word & 0xFF00U) == 0xFC00U) { uint8_t opcode = (uint8_t)(word & 0x00FFU); summary.command_words++; summary.command_histogram[opcode]++; } else if (word >= 0x8000U) { summary.extended_words++; } else if ((word >= 0x20U && word < 0x7FU) || word == '\t' || word == '\n' || word == '\r') { summary.literal_words++; wp_res_append_macro_preview_char(&summary, (uint8_t)word); } else { summary.control_words++; } } wp_res_finish_macro_command_stats(&summary); *out_summary = summary; } static void wp_res_analyze_keyboard_descriptors(const WpLoadedFile *file, WpKeyboardLayoutSummary *summary); static void wp_res_analyze_keyboard_sections(const WpLoadedFile *file, WpKeyboardLayoutSummary *summary) { size_t row; if (file == NULL || summary == NULL || file->prefix_bytes == NULL) { return; } for (row = WP_KEYBOARD_SECTION_DIRECTORY_OFFSET; row + WP_KEYBOARD_SECTION_ROW_SIZE <= file->prefix_size; row += WP_KEYBOARD_SECTION_ROW_SIZE) { uint16_t section_id = wp_res_le16(file->prefix_bytes + row); uint32_t section_size; uint16_t section_offset; bool valid; if (section_id == 0U) { break; } section_size = wp_res_le32(file->prefix_bytes + row + 2U); section_offset = wp_res_le16(file->prefix_bytes + row + 6U); summary->section_entries++; valid = section_offset < file->prefix_size && section_size <= (uint32_t)(file->prefix_size - section_offset); if (valid) { summary->valid_sections++; summary->section_payload_bytes += (size_t)section_size; if (section_id == WP_KEYBOARD_BINDING_SECTION_ID) { summary->has_binding_section = true; summary->binding_section_bytes = (size_t)section_size; } else if (section_id == WP_KEYBOARD_DESCRIPTOR_SECTION_ID) { summary->has_descriptor_section = true; summary->descriptor_section_bytes = (size_t)section_size; } } else { summary->invalid_sections++; } } } static void wp_res_analyze_keyboard_loaded(const WpLoadedFile *file, WpKeyboardLayoutSummary *out_summary) { WpKeyboardLayoutSummary summary; size_t offset; size_t table_bytes; size_t table_offset = WP_KEYBOARD_BINDING_TABLE_OFFSET; memset(&summary, 0, sizeof(summary)); if (file == NULL || file->prefix_bytes == NULL || file->prefix_size <= WP_KEYBOARD_BINDING_TABLE_OFFSET) { *out_summary = summary; return; } wp_res_analyze_keyboard_sections(file, &summary); if (!wp_res_find_prefix_section(file, WP_KEYBOARD_BINDING_SECTION_ID, &table_offset, &table_bytes)) { table_bytes = file->prefix_size - WP_KEYBOARD_BINDING_TABLE_OFFSET; } summary.trailing_bytes = table_bytes & 1U; summary.slots = table_bytes / 2U; for (offset = table_offset; offset + 1U < table_offset + table_bytes; offset += 2U) { uint16_t entry = wp_res_le16(file->prefix_bytes + offset); if (entry == 0U || entry == 0xFFFFU) { summary.empty_slots++; continue; } summary.bound_slots++; if (!wp_res_byte_bitmap_test(summary.entry_class_bitmap, (uint8_t)(entry >> 8U))) { summary.unique_entry_classes++; wp_res_byte_bitmap_add(summary.entry_class_bitmap, (uint8_t)(entry >> 8U)); } switch (entry & 0xFF00U) { case 0xFE00U: summary.literal_refs++; if (!wp_res_byte_bitmap_test(summary.literal_ref_bitmap, (uint8_t)entry)) { summary.unique_literal_refs++; wp_res_byte_bitmap_add(summary.literal_ref_bitmap, (uint8_t)entry); } break; case 0xFD00U: summary.macro_refs++; if (!wp_res_byte_bitmap_test(summary.macro_ref_bitmap, (uint8_t)entry)) { summary.unique_macro_refs++; wp_res_byte_bitmap_add(summary.macro_ref_bitmap, (uint8_t)entry); } break; case 0x8000U: summary.command_refs++; if (!wp_res_byte_bitmap_test(summary.command_ref_bitmap, (uint8_t)entry)) { summary.unique_command_refs++; wp_res_byte_bitmap_add(summary.command_ref_bitmap, (uint8_t)entry); } break; case 0xFC00U: summary.macro_command_refs++; if (!wp_res_byte_bitmap_test(summary.macro_command_ref_bitmap, (uint8_t)entry)) { summary.unique_macro_command_refs++; wp_res_byte_bitmap_add(summary.macro_command_ref_bitmap, (uint8_t)entry); } break; default: summary.other_refs++; break; } } wp_res_analyze_keyboard_descriptors(file, &summary); *out_summary = summary; } static bool wp_res_keyboard_descriptor_char(uint8_t ch) { return ch >= 0x20U && ch < 0x7FU; } static bool wp_res_keyboard_descriptor_candidate(const WpLoadedFile *file, size_t offset, size_t end, size_t *out_length) { size_t length; size_t i; if (file == NULL || file->prefix_bytes == NULL || out_length == NULL || offset + WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE > end) { return false; } length = file->prefix_bytes[offset + WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE - 1U]; if (length == 0U || length + 1U >= WP_RES_KEYBOARD_DESCRIPTOR_CAPACITY || offset + WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE + length > end) { return false; } if (file->prefix_bytes[offset + WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE + length - 1U] == 0U) { length--; if (length == 0U) { return false; } } for (i = 0U; i < length; ++i) { if (!wp_res_keyboard_descriptor_char( file->prefix_bytes[offset + WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE + i])) { return false; } } *out_length = length; return true; } static void wp_res_copy_keyboard_descriptor(char *dest, size_t capacity, const uint8_t *src, size_t length) { size_t copy_len; if (dest == NULL || capacity == 0U || src == NULL) { return; } copy_len = length; if (copy_len + 1U > capacity) { copy_len = capacity - 1U; } memcpy(dest, src, copy_len); dest[copy_len] = '\0'; } static void wp_res_analyze_keyboard_descriptors(const WpLoadedFile *file, WpKeyboardLayoutSummary *summary) { size_t section_offset; size_t section_size; size_t offset; size_t end; if (file == NULL || summary == NULL || !wp_res_find_prefix_section(file, WP_KEYBOARD_DESCRIPTOR_SECTION_ID, §ion_offset, §ion_size)) { return; } end = section_offset + section_size; for (offset = section_offset; offset + WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE < end; ++offset) { size_t length; const uint8_t *text; if (!wp_res_keyboard_descriptor_candidate(file, offset, end, &length)) { continue; } text = file->prefix_bytes + offset + WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE; summary->descriptor_records++; summary->descriptor_bytes += length; summary->last_descriptor_id = file->prefix_bytes[offset + 1U]; if (!summary->has_descriptor) { summary->has_descriptor = true; summary->first_descriptor_id = file->prefix_bytes[offset + 1U]; wp_res_copy_keyboard_descriptor(summary->first_descriptor, sizeof(summary->first_descriptor), text, length); } if (length > summary->longest_descriptor_length) { summary->longest_descriptor_length = length; wp_res_copy_keyboard_descriptor(summary->longest_descriptor, sizeof(summary->longest_descriptor), text, length); } offset += WP_KEYBOARD_DESCRIPTOR_HEADER_SIZE + length - 1U; } } static void wp_res_analyze_printer_loaded(const WpLoadedFile *file, WpPrinterResourceSummary *out_summary) { WpPrinterResourceSummary summary; size_t offset; memset(&summary, 0, sizeof(summary)); if (file == NULL || file->logical_bytes == NULL) { *out_summary = summary; return; } summary.odd_body_size = (file->logical_size & 1U) != 0U; summary.body_words = file->logical_size / 2U; if (file->logical_size >= 2U) { summary.header_word0 = wp_res_le16(file->logical_bytes); } if (file->logical_size >= 4U) { summary.header_word1 = wp_res_le16(file->logical_bytes + 2U); } if (file->logical_size >= 6U) { summary.header_word2 = wp_res_le16(file->logical_bytes + 4U); } if (file->logical_size >= WP_PRINTER_NAME_POINTER_WORD_OFFSET + 2U) { uint16_t name_offset = wp_res_le16(file->logical_bytes + WP_PRINTER_NAME_POINTER_WORD_OFFSET); summary.name_offset = name_offset; if (name_offset < file->logical_size) { size_t declared = file->logical_bytes[name_offset]; size_t available = file->logical_size - (size_t)name_offset - 1U; size_t name_bytes = declared < available ? declared : available; size_t i; summary.name_offset_valid = true; for (i = 0U; i < name_bytes; ++i) { uint8_t ch = file->logical_bytes[(size_t)name_offset + 1U + i]; if (ch == 0U) { break; } if (!wp_res_is_macro_title_char(ch)) { continue; } if (summary.name_length + 1U >= WP_RES_PRINTER_NAME_CAPACITY) { break; } summary.name[summary.name_length++] = (char)ch; summary.name[summary.name_length] = '\0'; } summary.has_name = summary.name_length != 0U; } } for (offset = 0U; offset + 1U < file->logical_size; offset += 2U) { uint16_t word = wp_res_le16(file->logical_bytes + offset); if (word == 0U) { summary.zero_words++; } else if (word == 0xFFFFU) { summary.ffff_words++; } else if ((size_t)word < file->logical_size) { summary.offset_like_words++; if (word > summary.highest_offset_like_word) { summary.highest_offset_like_word = word; } } } *out_summary = summary; } bool wp_res_analyze_file(const char *filename, WpResourceFileAnalysis *out_analysis) { WpLoadedFile file; WpResourceFileAnalysis analysis; if (filename == NULL || out_analysis == NULL) { return false; } if (!wp_file_load_body(filename, &file)) { return false; } memset(&analysis, 0, sizeof(analysis)); wp_res_info_from_loaded(&file, &analysis.info); wp_res_analyze_generic_loaded(&file, &analysis.generic); analysis.has_generic_summary = true; if (analysis.info.family == WP_RES_FAMILY_MACRO) { wp_res_analyze_macro_loaded(&file, &analysis.macro); analysis.has_macro_summary = true; } else if (analysis.info.family == WP_RES_FAMILY_KEYBOARD) { wp_res_analyze_keyboard_loaded(&file, &analysis.keyboard); analysis.has_keyboard_summary = true; } else if (analysis.info.family == WP_RES_FAMILY_PRINTER) { wp_res_analyze_printer_loaded(&file, &analysis.printer); analysis.has_printer_summary = true; } wp_file_free(&file); *out_analysis = analysis; return true; } void wp_res_load_printer(const char *filename) { WpResourceHeader head; if (!wp_res_read_header(filename, &head)) { return; } /* Placeholder for printer metrics loading. The validated header gives a * safe starting point for later parsing of PRS tables at data_offset. */ (void)head; } void wp_res_load_video(const char *filename) { WpResourceHeader head; if (!wp_res_read_header(filename, &head)) { return; } /* Placeholder for VRS video/resource tables after the common header. */ (void)head; }
Commit message
This repository is read-only for this account.
Repository snapshot
Current branch
main
Visibility
public
Your access
Read
Remote
None
File activity
View file history