diff --git a/error-mem-viewer/include/error-mem-viewer/crc.h b/error-mem-viewer/include/error-mem-viewer/crc.h new file mode 100644 index 0000000..70016cf --- /dev/null +++ b/error-mem-viewer/include/error-mem-viewer/crc.h @@ -0,0 +1,10 @@ +#ifndef _CRC_H_ +#define _CRC_H_ + +#include +#include + +uint32_t calculate_stm_crc(uint32_t *data, size_t len); + + +#endif /* _CRC_H_ */ diff --git a/error-mem-viewer/include/error-mem-viewer/err-mem-viewer-address-cell-renderer.h b/error-mem-viewer/include/error-mem-viewer/err-mem-viewer-address-cell-renderer.h deleted file mode 100644 index 7f9782e..0000000 --- a/error-mem-viewer/include/error-mem-viewer/err-mem-viewer-address-cell-renderer.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ -#define _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ - -#include - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE(ErrMemViewAddressCellRenderer, err_mem_view_address_cell_renderer, ERR_MEM_VIEW, ADDRESS_CELL_RENDERER, GtkCellRendererText) -#define ERR_MEM_VIEW_TYPE_ADDRESS_CELL_RENDERER (err_mem_view_address_cell_renderer_get_type()) - -GtkCellRenderer *err_mem_view_address_cell_renderer_new(); - -G_END_DECLS - -#endif /* _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ */ diff --git a/error-mem-viewer/include/error-mem-viewer/err-mem-viewer-hex-cell-renderer.h b/error-mem-viewer/include/error-mem-viewer/err-mem-viewer-hex-cell-renderer.h new file mode 100644 index 0000000..6ea5ae3 --- /dev/null +++ b/error-mem-viewer/include/error-mem-viewer/err-mem-viewer-hex-cell-renderer.h @@ -0,0 +1,15 @@ +#ifndef _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ +#define _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ + +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(ErrMemViewHexCellRenderer, err_mem_view_hex_cell_renderer, ERR_MEM_VIEW, HEX_CELL_RENDERER, GtkCellRendererText) +#define ERR_MEM_VIEW_TYPE_HEX_CELL_RENDERER (err_mem_view_address_cell_renderer_get_type()) + +GtkCellRenderer *err_mem_view_hex_cell_renderer_new(); + +G_END_DECLS + +#endif /* _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ */ diff --git a/error-mem-viewer/include/error-mem-viewer/safety-memory-types.h b/error-mem-viewer/include/error-mem-viewer/safety-memory-types.h new file mode 100644 index 0000000..4e55a58 --- /dev/null +++ b/error-mem-viewer/include/error-mem-viewer/safety-memory-types.h @@ -0,0 +1,57 @@ +#ifndef _SAFETY_MEM_TYPES_H_ +#define _SAFETY_MEM_TYPES_H_ + +#include + +/** + * @brief Magic number to signal a valid safety memory header. + */ +#define SAFETY_MEMORY_MAGIC 0x12AA5CB7 + +/** + * @brief Error memory NOP entry + */ +#define SAFETY_MEMORY_NOP_ENTRY 0xC1AA1222 + +/** + * @brief Safety memory header + */ +struct safety_memory_header { + uint32_t magic; /**< @brief Magic. Set to @ref SAFETY_MEMORY_MAGIC */ + uint32_t boot_status_offset; /**< @brief Offset of the safety_memory_boot_status struct (in 32 bit words)*/ + uint32_t config_overrides_offset; /**< @brief Offset address of override entries */ + uint32_t config_overrides_len; /**< @brief Length of override entries in words */ + uint32_t err_memory_offset; /**< @brief Offset of the error memory */ + uint32_t err_memory_end; /**< @brief End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */ + uint32_t crc; /**< @brief CRC of the header */ +}; + +struct safety_memory_boot_status { + /** + * @brief Reboot into the bootloader + * + * When this flag is set, the controller will load the bootloader to + * memory and execute it. + */ + uint32_t reboot_to_bootloader; + + /** + * @brief Bootloader has updated the code + * + * This flag is set, if the firmware ahs been updated successfully + */ + uint32_t code_updated; + + /** + * @brief reset_from_panic + * + * This flag is set, when entering the panic mode. + * Because the panic mode is reset by a watchdog reset, + * this flag is needed, in order to ensure, that the panic is handled correcly after + * the watchdog reset. + */ + uint32_t reset_from_panic; +}; + + +#endif /* _SAFETY_MEM_TYPES_H_ */ diff --git a/error-mem-viewer/resources/main-window.ui b/error-mem-viewer/resources/main-window.ui index a6767b0..9694161 100644 --- a/error-mem-viewer/resources/main-window.ui +++ b/error-mem-viewer/resources/main-window.ui @@ -6,18 +6,89 @@ main-window False - + True - True - in + False + vertical - + True - True - - + False + warning + True + False + + + + False + 6 + end + + + + + + + + + + + + False + False + 0 + + + + + False + 16 + + + + + + + + + + + + False + False + 0 + + + + + + False + True + 0 + + + + + True + True + in + + + True + True + + + + + + + + True + True + 1 + diff --git a/error-mem-viewer/src/crc.c b/error-mem-viewer/src/crc.c new file mode 100644 index 0000000..7421188 --- /dev/null +++ b/error-mem-viewer/src/crc.c @@ -0,0 +1,26 @@ +#include + +uint32_t do_crc(uint32_t init, uint32_t data) +{ + uint32_t crc = init; + uint32_t cnt; + + for(cnt=0; cnt < 32; cnt++) + { + crc = ((int32_t)(crc ^ data))<0 ? (crc << 1) ^ 0x04C11DB7 : crc << 1; + data <<=1; + } + + return crc; +} + +uint32_t calculate_stm_crc(uint32_t *data, size_t len) +{ + uint32_t crc = ~0U; + + while(len--) { + crc = do_crc(crc, *data++); + } + + return crc; +} diff --git a/error-mem-viewer/src/err-mem-viewer-address-cell-renderer.c b/error-mem-viewer/src/err-mem-viewer-hex-cell-renderer.c similarity index 71% rename from error-mem-viewer/src/err-mem-viewer-address-cell-renderer.c rename to error-mem-viewer/src/err-mem-viewer-hex-cell-renderer.c index 25da856..ea176d5 100644 --- a/error-mem-viewer/src/err-mem-viewer-address-cell-renderer.c +++ b/error-mem-viewer/src/err-mem-viewer-hex-cell-renderer.c @@ -1,7 +1,7 @@ -#include +#include #include -struct _ErrMemViewAddressCellRenderer { +struct _ErrMemViewHexCellRenderer { GtkCellRendererText base; }; @@ -10,9 +10,9 @@ enum { PROP_COUNT }; -G_DEFINE_TYPE(ErrMemViewAddressCellRenderer, err_mem_view_address_cell_renderer, GTK_TYPE_CELL_RENDERER_TEXT) +G_DEFINE_TYPE(ErrMemViewHexCellRenderer, err_mem_view_address_cell_renderer, GTK_TYPE_CELL_RENDERER_TEXT) -static void err_mem_view_address_cell_renderer_init(ErrMemViewAddressCellRenderer *self) +static void err_mem_view_address_cell_renderer_init(ErrMemViewHexCellRenderer *self) { (void)self; } @@ -28,7 +28,7 @@ static void convert_gvalue_uint_to_string(const GValue *in, GValue *out) g_value_init(out, G_TYPE_STRING); val = g_value_get_uint(in); - snprintf(text, sizeof(text), "0x%08x", val); + snprintf(text, sizeof(text), "0x%08X", val); g_value_set_string(out, text); } @@ -64,7 +64,7 @@ static void err_mem_view_address_cell_renderer_get_property(GObject *object static GParamSpec *properties[PROP_COUNT]; -void err_mem_view_address_cell_renderer_class_init(ErrMemViewAddressCellRendererClass *klass) +void err_mem_view_address_cell_renderer_class_init(ErrMemViewHexCellRendererClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS(klass); @@ -75,7 +75,7 @@ void err_mem_view_address_cell_renderer_class_init(ErrMemViewAddressCellRenderer g_object_class_install_properties(oclass, PROP_COUNT, properties); } -GtkCellRenderer *err_mem_view_address_cell_renderer_new() +GtkCellRenderer *err_mem_view_hex_cell_renderer_new() { - return GTK_CELL_RENDERER(g_object_new(ERR_MEM_VIEW_TYPE_ADDRESS_CELL_RENDERER, NULL)); + return GTK_CELL_RENDERER(g_object_new(ERR_MEM_VIEW_TYPE_HEX_CELL_RENDERER, NULL)); } diff --git a/error-mem-viewer/src/main.c b/error-mem-viewer/src/main.c index a59e7ea..f0194c6 100644 --- a/error-mem-viewer/src/main.c +++ b/error-mem-viewer/src/main.c @@ -3,15 +3,18 @@ #include #include #include -#include #include - -#include +#include +#include +#include +#include +#include enum memory_view_columns { MEM_VIEW_COL_ADDRESS = 0, MEM_VIEW_COL_DATA, MEM_VIEW_COL_INTERPRETATION, + MEM_VIEW_COLOR, MEM_VIEW_COLUMN_COUNT }; @@ -19,87 +22,427 @@ struct application_data { GtkWidget *borrowed_main_window; GtkTreeView *borrowed_tree_view; GtkHeaderBar *borrowed_header_bar; + GtkInfoBar *borrowed_info_bar; const char *error_memory_data; size_t file_size; }; -static void tree_view_show_error_memory(GtkTreeView *tree_view, const char *memory, size_t len) -{ - GtkListStore *store; - GtkTreeIter _iter; - GtkTreeIter *iter = &_iter; +enum entry_state { + ENTRY_STATE_VALID = 0, + ENTRY_STATE_INVALID, + ENTRY_STATE_NOT_PARSED, + ENTRY_STATE_BLOCK +}; - store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view)); - if (store) { - for (unsigned int i = 0; i < 0xFFFF; i+=4) { - gtk_list_store_append(store, iter); - gtk_list_store_set(store, iter, MEM_VIEW_COL_ADDRESS, i, MEM_VIEW_COL_INTERPRETATION, "Test", MEM_VIEW_COL_DATA, i % 12, -1); - } +static void append_data(GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, GString *interpretation, enum entry_state state, uint32_t addr, uint32_t val) +{ + const GdkRGBA invalid_color = { + .red = 1.0, + .alpha = 1.0, + .green = 0.0, + .blue = 0.0, + }; + + const GdkRGBA valid_color = { + .red = 0.0, + .alpha = 1.0, + .green = 1.0, + .blue = 0.0, + }; + + const GdkRGBA warn_color = { + .red = 1.0, + .alpha = 1.0, + .green = 1.0, + .blue = 0.0, + }; + + const GdkRGBA block_color = { + .red = 1.0, + .alpha = 1.0, + .green = 0.3, + .blue = 0.5, + }; + + const GdkRGBA *col; + + switch (state) { + case ENTRY_STATE_VALID: + col = &valid_color; + break; + case ENTRY_STATE_NOT_PARSED: + col = &warn_color; + break; + case ENTRY_STATE_BLOCK: + col = &block_color; + break; + case ENTRY_STATE_INVALID: + default: + col = &invalid_color; + break; } + gtk_tree_store_append(store, iter, parent); + gtk_tree_store_set(store, iter, MEM_VIEW_COL_ADDRESS, addr, MEM_VIEW_COLOR, col, MEM_VIEW_COL_INTERPRETATION, (interpretation ? interpretation->str : ""), MEM_VIEW_COL_DATA, val, -1); + if (interpretation) + g_string_free(interpretation, TRUE); +} + +static void info_bar_show_message(GtkInfoBar *info_bar, GtkMessageType type, const char *format, ...) +{ + GString *string; + va_list args; + GtkWidget *content; + GList *children, *iter; + GtkWidget *label; + + va_start(args, format); + string = g_string_new(NULL); + + g_string_vprintf(string, format, args); + va_end(args); + + content = gtk_info_bar_get_content_area(info_bar); + children = gtk_container_get_children(GTK_CONTAINER(content)); + for (iter = children; iter; iter = g_list_next(iter)) + gtk_widget_destroy(GTK_WIDGET(iter->data)); + g_list_free(children); + + label = gtk_label_new(string->str); + gtk_container_add(GTK_CONTAINER(content), label); + gtk_widget_show(label); + gtk_info_bar_set_message_type(info_bar, type); + gtk_widget_show(GTK_WIDGET(info_bar)); + gtk_info_bar_set_revealed(info_bar, TRUE); + + g_string_free(string, TRUE); +} + +static bool check_err_mem_header(struct safety_memory_header *header, uint32_t *expected) +{ + uint32_t crc; + + crc = calculate_stm_crc((uint32_t *)header, sizeof(*header) / sizeof(uint32_t) - 1); + if (expected) + *expected = crc; + if (crc == header->crc) + return true; + else + return false; +} + +static GString *new_string_printf(const char *fmt, ...) +{ + GString *string; + va_list args; + + va_start(args, fmt); + + string = g_string_new(NULL); + g_string_vprintf(string, fmt, args); + + va_end(args); + + return string; +} + +static void show_error_memory(GtkTreeView *tree_view, const unsigned char *memory, size_t len) +{ + GtkTreeStore *store; + GtkTreeIter iter; + GtkTreeIter iter2; + GtkTreeIter *parent_iter, *child_iter; + enum entry_state state; + GString *interpret; + bool valid; + bool analyze_further = true; + struct safety_memory_header header; + unsigned int i; + uint32_t dat, expected_value; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view)); + gtk_tree_store_clear(store); + + for (i = 0; i < len / 4; i++) { + valid = false; + interpret = NULL; + dat = memory[i*4]; + dat |= ((uint32_t)memory[i*4+1]) << 8; + dat |= ((uint32_t)memory[i*4+2]) << 16; + dat |= ((uint32_t)memory[i*4+3]) << 24; + + state = ENTRY_STATE_NOT_PARSED; + + if (!analyze_further) { + interpret = g_string_new("Not analyzed due to previous error"); + state = ENTRY_STATE_NOT_PARSED; + parent_iter = NULL; + child_iter = &iter; + goto print; + } + /* Parse header */ + switch (i) { + case 0: /* Magic */ + parent_iter = NULL; + child_iter = &iter; + append_data(store, parent_iter, child_iter, new_string_printf("Header"), ENTRY_STATE_BLOCK, i * 4, dat); + parent_iter = child_iter; + child_iter = &iter2; + if (dat == SAFETY_MEMORY_MAGIC) { + valid = true; + interpret = g_string_new("Valid Header Magic"); + } else { + interpret = g_string_new("Invalid Header Magic"); + analyze_further = false; + } + header.magic = dat; + break; + case 1: + header.boot_status_offset = dat; + + valid = true; + interpret = new_string_printf("Boot status offset addr: %u", dat); + break; + case 2: + header.config_overrides_offset = dat; + interpret = new_string_printf("Config override offset addr: %u", dat); + valid = true; + break; + case 3: + header.config_overrides_len = dat; + interpret = new_string_printf("Config override length: %u", dat); + valid = true; + break; + case 4: + header.err_memory_offset = dat; + interpret = new_string_printf("Error memory offset addr: %u", dat); + valid = true; + break; + case 5: + header.err_memory_end = dat; + interpret = new_string_printf("Error memory end ptr: %u", dat); + valid = true; + break; + case 6: + header.crc = dat; + valid = check_err_mem_header(&header, &expected_value); + if (valid) { + interpret = new_string_printf("Header CRC: 0x%08X", dat); + } else { + + interpret = new_string_printf("Invalid CRC: 0x%08X | Expected: 0x%08X", dat, expected_value); + analyze_further = false; + } + break; + } + if (i <= 6) { + state = ENTRY_STATE_INVALID; + goto print; + } + + /* Check if we're in the boot status structure */ + if (i >= header.boot_status_offset && i < header.boot_status_offset + sizeof(struct safety_memory_boot_status) / 4) { + if (i == header.boot_status_offset) { + /* First entry of boot status struct */ + parent_iter = NULL; + child_iter = &iter; + interpret = new_string_printf("Boot Status Struct"); + append_data(store, parent_iter, child_iter, interpret, ENTRY_STATE_BLOCK, i * 4, dat); + parent_iter = &iter; + child_iter = &iter2; + interpret = NULL; + } + switch (i - header.boot_status_offset) { + case 0: + interpret = new_string_printf("%s into bootloader", (dat ? "Boot" : "Do not boot")); + valid = true; + break; + case 1: + interpret = new_string_printf("Code %s been updated", (dat ? "has" : "hasn't")); + valid = true; + break; + case 2: + interpret = new_string_printf("Panic %s", (dat ? "occured" : "did not occur")); + valid = true; + break; + default: + valid = false; + interpret = new_string_printf("Value undefined!"); + break; + } + state = ENTRY_STATE_INVALID; + goto print; + } + + if (i >= header.config_overrides_offset && i < (header.config_overrides_offset + header.config_overrides_len)) { + if (i == header.config_overrides_offset) { + parent_iter = NULL; + child_iter = &iter; + append_data(store, parent_iter, child_iter, + new_string_printf("Config Overrides"), + ENTRY_STATE_BLOCK, i * 4, dat); + parent_iter = child_iter; + child_iter = &iter2; + } + + interpret = new_string_printf("Config Overrides not yet implemented"); + valid = true; + state = ENTRY_STATE_INVALID; + goto print; + } + + if (i >= header.err_memory_offset && i < header.err_memory_end) { + if (i == header.err_memory_offset) { + parent_iter = NULL; + child_iter = &iter; + append_data(store, parent_iter, child_iter, + new_string_printf("Error Memory (length: %d)", header.err_memory_end - header.err_memory_offset), + ENTRY_STATE_BLOCK, i * 4, dat); + parent_iter = child_iter; + child_iter = &iter2; + } + + switch (dat) { + case SAFETY_MEMORY_NOP_ENTRY: + valid = true; + interpret = new_string_printf("Error Memory NOP"); + break; + default: + if ((dat & 0xFFU) == 0x51U) { + valid = true; + interpret = new_string_printf("Err memory Entry"); + } else + interpret = new_string_printf("Invalid error memory entry"); + break; + } + state = ENTRY_STATE_INVALID; + goto print; + } + + if (i == header.err_memory_end) { + expected_value = calculate_stm_crc((uint32_t *)memory, header.err_memory_end); + if (expected_value == dat) { + interpret = new_string_printf("Valid Memory CRC"); + valid = true; + } else { + interpret = new_string_printf("Invalid CRC. Expected: 0x%08X", expected_value); + } + state = ENTRY_STATE_INVALID; + parent_iter = NULL; + child_iter = &iter; + goto print; + } + + parent_iter = NULL; + child_iter = &iter; +print: + if (state != ENTRY_STATE_NOT_PARSED) { + state = (valid ? ENTRY_STATE_VALID : ENTRY_STATE_INVALID); + } + append_data(store, parent_iter, child_iter, interpret, state, i * 4, dat); + } + + + if (!store) + return; } void setup_tree_view(GtkTreeView *tree_view) { - GtkListStore *list_store; + GtkTreeStore *tree_store; GtkCellRenderer *string_renderer; GtkCellRenderer *hex_renderer; GtkTreeViewColumn *column; g_return_if_fail(GTK_IS_TREE_VIEW(tree_view)); - list_store = gtk_list_store_new(MEM_VIEW_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); - gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(list_store)); + tree_store = gtk_tree_store_new(MEM_VIEW_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, GDK_TYPE_RGBA); + gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(tree_store)); string_renderer = gtk_cell_renderer_text_new(); - hex_renderer = err_mem_view_address_cell_renderer_new(); + hex_renderer = err_mem_view_hex_cell_renderer_new(); - column = gtk_tree_view_column_new_with_attributes("Address", hex_renderer, "hex-num", MEM_VIEW_COL_ADDRESS, NULL); + column = gtk_tree_view_column_new_with_attributes("Address", hex_renderer, "hex-num", MEM_VIEW_COL_ADDRESS, "foreground-rgba", MEM_VIEW_COLOR, NULL); gtk_tree_view_append_column(tree_view, column); - column = gtk_tree_view_column_new_with_attributes("Data", hex_renderer, "hex-num", MEM_VIEW_COL_DATA, NULL); + column = gtk_tree_view_column_new_with_attributes("Data", hex_renderer, "hex-num", MEM_VIEW_COL_DATA, "foreground-rgba", MEM_VIEW_COLOR, NULL); gtk_tree_view_append_column(tree_view, column); - column = gtk_tree_view_column_new_with_attributes("Interpretation", string_renderer, "text", MEM_VIEW_COL_INTERPRETATION, NULL); + column = gtk_tree_view_column_new_with_attributes("Interpretation", string_renderer, "text", MEM_VIEW_COL_INTERPRETATION, "foreground-rgba", MEM_VIEW_COLOR, NULL); gtk_tree_view_append_column(tree_view, column); } -static int64_t get_file_size(int file) +static ptrdiff_t get_file_size(const char *filename) { - off_t size; + struct stat file_stat; + int res; + ptrdiff_t size; + res = stat(filename, &file_stat); - if (!file) - return -1L; + if (res) + size = -1UL; + else + size = (ptrdiff_t)file_stat.st_size; - size = lseek(file, 0, SEEK_END); - if (size == (off_t)-1) - return -1; + return size; - - return (int64_t)size; } -static const char *map_file_to_memory(const char *name, size_t *detected_size) +/** + * @brief Loads a file to memory and decodes it using base64 + * @param name Filename + * @param detected_size detected Size of the ouput generated + * @return Buffer of decoded data with size \p detected_size, NULL in case of an error. + * @note Return buffer is allocated on heap. Use free() to free the memory. + */ +static char *load_file_to_memory(const char *name, size_t *detected_size) { - const char *ret = NULL; - int file; - int64_t file_size; + ptrdiff_t size; + FILE *f; + char *ret = NULL; + size_t read_bytes; + gsize decoded_size; - file = open(name, O_RDONLY); - if (file < 0) + size = get_file_size(name); + if (size <= 0) goto exit; - file_size = get_file_size(file); - if (file_size < 0) - goto exit; + f = fopen(name, "rb"); + ret = (char *)malloc(size + 1); + if (!ret) + goto exit_close_file; + + read_bytes = fread(ret, 1UL, (size_t)size, f); + if (read_bytes != (size_t)size) { + free(ret); + ret = NULL; + goto exit_close_file; + } + + ret[size] = '\0'; + + g_base64_decode_inplace(ret, &decoded_size); + if (decoded_size == 0) { + free(ret); + ret = NULL; + goto exit_close_file; + } if (detected_size) - *detected_size = (size_t)file_size; - - ret = (const char *)mmap(NULL, (size_t)file_size, PROT_READ, MAP_SHARED, file, (off_t)0L); + *detected_size = (size_t)decoded_size; +exit_close_file: + fclose(f); exit: return ret; } +G_MODULE_EXPORT +void info_bar_close_cb(GtkInfoBar *info_bar, gpointer user_data) +{ + (void)user_data; + + gtk_info_bar_set_revealed(info_bar, FALSE); +} + G_MODULE_EXPORT void open_button_clicked_cb(GtkButton *button, gpointer *user_data) { @@ -109,15 +452,18 @@ void open_button_clicked_cb(GtkButton *button, gpointer *user_data) gint res; gchar *filename; + dialog = GTK_DIALOG(gtk_file_chooser_dialog_new("Open File", GTK_WINDOW(data->borrowed_main_window), - GTK_FILE_CHOOSER_ACTION_OPEN, - "Cancel", GTK_RESPONSE_CANCEL, - "Open", GTK_RESPONSE_ACCEPT, - NULL)); + GTK_FILE_CHOOSER_ACTION_OPEN, + "Cancel", GTK_RESPONSE_CANCEL, + "Open", GTK_RESPONSE_ACCEPT, + NULL)); res = gtk_dialog_run(dialog); if (res == GTK_RESPONSE_ACCEPT) { filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - data->error_memory_data = map_file_to_memory(filename, &data->file_size); + if (data->error_memory_data) + free((void *)data->error_memory_data); + data->error_memory_data = load_file_to_memory(filename, &data->file_size); if (!data->error_memory_data) { g_warning("File could not be loaded"); g_free(filename); @@ -126,31 +472,41 @@ void open_button_clicked_cb(GtkButton *button, gpointer *user_data) gtk_header_bar_set_subtitle(data->borrowed_header_bar, filename); g_free(filename); + } else { + goto ret_destroy_dialog; } - tree_view_show_error_memory(data->borrowed_tree_view, data->error_memory_data, data->file_size); + if (data->file_size % 4 || data->file_size == 0) { + free((void *)data->error_memory_data); + data->error_memory_data = NULL; + info_bar_show_message(data->borrowed_info_bar, GTK_MESSAGE_WARNING, + "Data must be base64 encoded and must contain full 32 bit words of data."); + goto ret_destroy_dialog; + } + + show_error_memory(data->borrowed_tree_view, (const unsigned char *)data->error_memory_data, data->file_size); ret_destroy_dialog: - gtk_widget_destroy(GTK_WIDGET(dialog)); + gtk_widget_destroy(GTK_WIDGET(dialog)); } static void app_activated(GApplication *app, gpointer user_data) { - (void)user_data; + struct application_data *data = (struct application_data *)user_data; GtkBuilder *builder; GtkWindow *main_window; - static struct application_data data; builder = gtk_builder_new_from_resource("/gui/main"); main_window = GTK_WINDOW(gtk_builder_get_object(builder, "main-window")); - data.borrowed_main_window = GTK_WIDGET(main_window); - data.error_memory_data = NULL; - data.borrowed_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "error-mem-tree-view")); - data.borrowed_header_bar = GTK_HEADER_BAR(gtk_builder_get_object(builder, "header-bar")); + data->borrowed_main_window = GTK_WIDGET(main_window); + data->error_memory_data = NULL; + data->borrowed_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "error-mem-tree-view")); + data->borrowed_header_bar = GTK_HEADER_BAR(gtk_builder_get_object(builder, "header-bar")); + data->borrowed_info_bar = GTK_INFO_BAR(gtk_builder_get_object(builder, "info-bar")); - setup_tree_view(data.borrowed_tree_view); + setup_tree_view(data->borrowed_tree_view); - gtk_builder_connect_signals(builder, &data); + gtk_builder_connect_signals(builder, data); g_object_unref(builder); gtk_application_add_window(GTK_APPLICATION(app), main_window); gtk_widget_show(GTK_WIDGET(main_window)); @@ -160,14 +516,17 @@ static int start_gui(int argc, char **argv) { int ret = 0; GtkApplication *g_app; + static struct application_data data; /* Create a new G application which will start a completely new process for each call to the program (NON_UNIQUE) instead * of creating an additional window in the same process */ g_app = gtk_application_new("de.shimatta.reflow.error-mem-viewer", G_APPLICATION_NON_UNIQUE); - g_signal_connect(g_app, "activate", G_CALLBACK(app_activated), NULL); + g_signal_connect(g_app, "activate", G_CALLBACK(app_activated), &data); ret = g_application_run(G_APPLICATION(g_app), argc, argv); g_object_unref(g_app); + if (data.error_memory_data) + free((void *)data.error_memory_data); return ret; }