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_file_format.c
File editor
#include "wp_file_format.h" #include "wp_layout_engine.h" #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> static uint32_t wp_le32(const uint8_t *p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); } bool wp_file_signature_is_valid(const uint8_t signature[4]) { return signature != NULL && signature[0] == 0xFF && signature[1] == 'W' && signature[2] == 'P' && signature[3] == 'C'; } const char *wp_file_type_name(uint8_t file_type) { switch (file_type) { case 0x01: return "Macro"; case 0x02: return "Video/resource data"; case 0x03: return "Keyboard layout"; case 0x0A: return "Document/style"; case 0x0B: return "Speller dictionary"; case 0x0C: return "Speller data"; case 0x10: return "Printer resource"; case 0x15: return "Overlay/help resource"; case 0x16: return "Graphic"; case 0x19: return "Macro resource"; case 0x1E: return "Quick reference resource"; case 0x24: return "Video resource"; case 0x2A: return "Index/resource data"; default: return "Unknown"; } } static bool wp_file_size(FILE *f, long *out_size) { long cur; long end; if (f == NULL || out_size == NULL) { return false; } cur = ftell(f); if (cur < 0) { return false; } if (fseek(f, 0, SEEK_END) != 0) { return false; } end = ftell(f); if (end < 0) { return false; } if (fseek(f, cur, SEEK_SET) != 0) { return false; } *out_size = end; return true; } static uint32_t wp_file_fnv1a_update(uint32_t hash, const uint8_t *bytes, size_t size) { size_t i; if (bytes == NULL && size != 0U) { return hash; } for (i = 0; i < size; ++i) { hash ^= (uint32_t)bytes[i]; hash *= 16777619U; } return hash; } static bool wp_file_prefix_header_matches(const WpLoadedFile *file) { const uint8_t *prefix; if (file == NULL || file->prefix_bytes == NULL || file->prefix_size < WP_FILE_PREFIX_SIZE) { return false; } prefix = file->prefix_bytes; return memcmp(prefix, file->header.signature, sizeof(file->header.signature)) == 0 && wp_le32(prefix + 4) == file->header.data_offset && prefix[8] == file->header.product_type && prefix[9] == file->header.file_type && prefix[10] == file->header.major_version && prefix[11] == file->header.minor_version; } static char *wp_file_temp_path(const char *filename) { static const char suffix[] = ".wp-tmp"; size_t filename_len; size_t suffix_len; char *path; if (filename == NULL) { return NULL; } filename_len = strlen(filename); suffix_len = sizeof(suffix) - 1U; if (filename_len > ((size_t)-1) - suffix_len - 1U) { return NULL; } path = (char *)malloc(filename_len + suffix_len + 1U); if (path == NULL) { return NULL; } memcpy(path, filename, filename_len); memcpy(path + filename_len, suffix, suffix_len); path[filename_len + suffix_len] = '\0'; return path; } bool wp_file_validate_loaded(const WpLoadedFile *file, uint32_t *out_flags) { uint32_t flags = WP_FILE_VALIDATE_OK; uint64_t expected_size = 0U; if (file == NULL) { flags |= WP_FILE_VALIDATE_NULL_FILE; if (out_flags != NULL) { *out_flags = flags; } return false; } if (!wp_file_signature_is_valid(file->header.signature)) { flags |= WP_FILE_VALIDATE_BAD_SIGNATURE; } if (file->prefix_bytes == NULL) { flags |= WP_FILE_VALIDATE_PREFIX_NULL; } if (file->prefix_size < WP_FILE_PREFIX_SIZE) { flags |= WP_FILE_VALIDATE_PREFIX_TOO_SMALL; } if (file->logical_size != 0U && file->logical_bytes == NULL) { flags |= WP_FILE_VALIDATE_BODY_NULL; } if (file->prefix_size != (size_t)file->header.data_offset) { flags |= WP_FILE_VALIDATE_OFFSET_MISMATCH; } expected_size = (uint64_t)file->prefix_size + (uint64_t)file->logical_size; if (expected_size > UINT32_MAX) { flags |= WP_FILE_VALIDATE_SIZE_OVERFLOW; } else if (file->header.file_size != (uint32_t)expected_size) { flags |= WP_FILE_VALIDATE_SIZE_MISMATCH; } if (!wp_file_prefix_header_matches(file)) { flags |= WP_FILE_VALIDATE_HEADER_BYTES_MISMATCH; } if (out_flags != NULL) { *out_flags = flags; } return flags == WP_FILE_VALIDATE_OK; } bool wp_file_preservation_info(const WpLoadedFile *file, WpFilePreservationInfo *out_info) { WpFilePreservationInfo info; uint32_t hash; if (out_info == NULL) { return false; } memset(&info, 0, sizeof(info)); if (file == NULL) { info.validation_flags = WP_FILE_VALIDATE_NULL_FILE; *out_info = info; return false; } wp_file_validate_loaded(file, &info.validation_flags); info.prefix_size = file->prefix_size; info.body_size = file->logical_size; info.data_offset = file->header.data_offset; info.file_size = file->header.file_size; info.has_body = file->logical_size != 0U; hash = 2166136261U; info.prefix_hash = wp_file_fnv1a_update(hash, file->prefix_bytes, file->prefix_size); info.body_hash = wp_file_fnv1a_update(hash, file->logical_bytes, file->logical_size); hash = wp_file_fnv1a_update(hash, file->prefix_bytes, file->prefix_size); info.full_hash = wp_file_fnv1a_update(hash, file->logical_bytes, file->logical_size); info.byte_stable = info.validation_flags == WP_FILE_VALIDATE_OK; *out_info = info; return true; } bool wp_file_read_header(const char *filename, WpFileHeader *out_header) { FILE *f; uint8_t raw[WP_FILE_PREFIX_SIZE]; long size = 0; WpFileHeader header; if (filename == NULL || out_header == NULL) { return false; } f = fopen(filename, "rb"); if (f == NULL) { return false; } if (!wp_file_size(f, &size) || size < (long)WP_FILE_PREFIX_SIZE || (uint64_t)size > UINT32_MAX) { fclose(f); return false; } if (fread(raw, 1, sizeof(raw), f) != sizeof(raw)) { fclose(f); return false; } fclose(f); memset(&header, 0, sizeof(header)); memcpy(header.signature, raw, sizeof(header.signature)); header.data_offset = wp_le32(raw + 4); header.product_type = raw[8]; header.file_type = raw[9]; header.major_version = raw[10]; header.minor_version = raw[11]; header.file_size = (uint32_t)size; if (!wp_file_signature_is_valid(header.signature)) { return false; } if (header.data_offset < WP_FILE_PREFIX_SIZE || (uint64_t)header.data_offset > (uint64_t)header.file_size) { return false; } *out_header = header; return true; } bool wp_file_load_body(const char *filename, WpLoadedFile *out_file) { FILE *f; WpFileHeader header; size_t body_size; if (filename == NULL || out_file == NULL) { return false; } memset(out_file, 0, sizeof(*out_file)); if (!wp_file_read_header(filename, &header)) { return false; } body_size = (size_t)(header.file_size - header.data_offset); out_file->header = header; out_file->prefix_size = (size_t)header.data_offset; out_file->logical_size = body_size; out_file->prefix_bytes = (uint8_t *)malloc(out_file->prefix_size); if (out_file->prefix_bytes == NULL) { wp_file_free(out_file); return false; } f = fopen(filename, "rb"); if (f == NULL) { wp_file_free(out_file); return false; } if (fread(out_file->prefix_bytes, 1, out_file->prefix_size, f) != out_file->prefix_size) { fclose(f); wp_file_free(out_file); return false; } if (body_size != 0U) { out_file->logical_bytes = (uint8_t *)malloc(body_size); if (out_file->logical_bytes == NULL) { fclose(f); wp_file_free(out_file); return false; } if (fread(out_file->logical_bytes, 1, body_size, f) != body_size) { fclose(f); wp_file_free(out_file); return false; } } fclose(f); if (!wp_file_validate_loaded(out_file, NULL)) { wp_file_free(out_file); return false; } return wp_file_rebuild_lifo(out_file); } bool wp_file_rebuild_lifo(WpLoadedFile *file) { uint8_t *lifo; size_t i; if (file == NULL) { return false; } if (file->logical_size != 0U && file->logical_bytes == NULL) { return false; } free(file->lifo_storage); file->lifo_storage = NULL; if (file->logical_size == 0U) { return true; } lifo = (uint8_t *)malloc(file->logical_size); if (lifo == NULL) { return false; } for (i = 0; i < file->logical_size; ++i) { lifo[i] = file->logical_bytes[file->logical_size - 1U - i]; } file->lifo_storage = lifo; return true; } bool wp_file_replace_body(WpLoadedFile *file, const uint8_t *body, size_t body_size) { uint8_t *new_body = NULL; uint64_t new_size; if (file == NULL || (body_size != 0U && body == NULL)) { return false; } new_size = (uint64_t)file->header.data_offset + (uint64_t)body_size; if (new_size > UINT32_MAX) { return false; } if (body_size != 0U) { new_body = (uint8_t *)malloc(body_size); if (new_body == NULL) { return false; } memcpy(new_body, body, body_size); } free(file->logical_bytes); file->logical_bytes = new_body; file->logical_size = body_size; file->header.file_size = (uint32_t)new_size; if (!wp_file_rebuild_lifo(file)) { return false; } return true; } bool wp_file_write_stream(FILE *stream, const WpLoadedFile *file) { size_t written; if (stream == NULL || !wp_file_validate_loaded(file, NULL)) { return false; } written = fwrite(file->prefix_bytes, 1, file->prefix_size, stream); if (written != file->prefix_size) { return false; } if (file->logical_size != 0U) { written = fwrite(file->logical_bytes, 1, file->logical_size, stream); if (written != file->logical_size) { return false; } } return true; } bool wp_file_save(const char *filename, const WpLoadedFile *file) { FILE *f; bool ok; if (filename == NULL || !wp_file_validate_loaded(file, NULL)) { return false; } f = fopen(filename, "wb"); if (f == NULL) { return false; } ok = wp_file_write_stream(f, file); if (fclose(f) != 0) { return false; } return ok; } bool wp_file_save_atomic(const char *filename, const WpLoadedFile *file) { FILE *f; FILE *probe; char *temp_path; bool ok; if (filename == NULL || !wp_file_validate_loaded(file, NULL)) { return false; } temp_path = wp_file_temp_path(filename); if (temp_path == NULL) { return false; } probe = fopen(temp_path, "rb"); if (probe != NULL) { fclose(probe); free(temp_path); return false; } f = fopen(temp_path, "wb"); if (f == NULL) { free(temp_path); return false; } ok = wp_file_write_stream(f, file); if (fclose(f) != 0) { ok = false; } if (!ok) { remove(temp_path); free(temp_path); return false; } if (rename(temp_path, filename) != 0) { remove(temp_path); free(temp_path); return false; } free(temp_path); return true; } void wp_file_free(WpLoadedFile *file) { if (file == NULL) { return; } free(file->prefix_bytes); free(file->logical_bytes); free(file->lifo_storage); memset(file, 0, sizeof(*file)); } bool wp_file_bind_primary_stream(WpLoadedFile *file, WpLayoutGlobals *wl, uint32_t buffer_space) { if (file == NULL || wl == NULL) { return false; } if (file->logical_size > (size_t)INT_MAX) { return false; } if (file->logical_size > 0 && file->lifo_storage == NULL) { return false; } wp_primary_buffer_bind_bytes_lifo(wl, file->lifo_storage, (int)file->logical_size, buffer_space); return true; }
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