📄 gtk.c
字号:
#include <gtk/gtk.h>#include <glib/gthread.h>#include <glade/glade.h>#include <string.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/stat.h>#include <sys/types.h>#include <errno.h>#include "grf.h"GladeXML *xml;Grf *grf = NULL;GString *filename;GtkWidget *opensel = NULL, *savesel = NULL, *dirsel = NULL;GtkTreeModel *filelist;GtkTreePath *current_selection = NULL;gboolean filling = FALSE;GdkCursor *busy_cursor = NULL;typedef struct { GThread *thread; enum { STATUS_INIT, STATUS_MKDIR, STATUS_EXTRACT, STATUS_DONE } status; unsigned long max; unsigned long current; unsigned long failed; char file[PATH_MAX]; gboolean stop;} ExtractProgress;ExtractProgress extractProgress, lastKnownProgress;GStaticMutex extractProgressM;enum { INDEX_COL, DISPLAY_COL, TYPE_COL, SIZE_COL, SIZE_DISPLAY_COL, N_COLS};#define W(x) glade_xml_get_widget (xml, #x)#define _(x) x/*********************** * Utility functions ***********************/static voidshow_error (gchar *format, ...){ GtkWidget *dialog; va_list ap; gchar *msg; va_start (ap, format); msg = g_strdup_vprintf (format, ap); va_end (ap); dialog = gtk_message_dialog_new (GTK_WINDOW (W(main)), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, msg); gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); g_free (msg);}static GladeXML *load_glade (gchar *basename){ GladeXML *xml; char self[PATH_MAX + 1]; GList *search_dirs = NULL, *dir; gchar *filename = NULL; /* Locate itself if we're on Linux */ if (realpath ("/proc/self/exe", self)) { char *dir; dir = g_path_get_dirname (self); search_dirs = g_list_append (search_dirs, dir); search_dirs = g_list_append (search_dirs, g_strdup_printf ("%s/../share/grftool", dir)); } search_dirs = g_list_append (search_dirs, g_strdup (".")); for (dir = search_dirs; dir; dir = dir->next) { gchar *fn; fn = g_strdup_printf ("%s/%s", (gchar *) dir->data, basename); if (g_file_test (fn, G_FILE_TEST_IS_REGULAR)) { filename = fn; break; } g_free (fn); } g_list_foreach (search_dirs, (GFunc) g_free, NULL); g_list_free (search_dirs); if (!filename) { show_error (_("Unable to initialize the user interface. You may have to re-install this software.")); exit (5); } xml = glade_xml_new (filename, NULL, NULL); if (!xml) { show_error (_("Unable to initialize the user interface. You may have to re-install this software.")); exit (5); } glade_xml_signal_autoconnect (xml); return xml;}static voidset_status (const char *msg){ static guint ctx = 0; GtkStatusbar *bar; bar = GTK_STATUSBAR (W(status)); gtk_statusbar_pop (bar, ctx); ctx = gtk_statusbar_get_context_id (bar, msg); gtk_statusbar_push (bar, ctx, msg);}static voidmkdirs (const char *dir){ gchar **paths; GString *str; gint i = 0; paths = g_strsplit (dir, G_DIR_SEPARATOR_S, -1); str = g_string_new (""); while (paths[i]) { if (!*paths[i]) { i++; continue; } if (i > 0) g_string_append_c (str, G_DIR_SEPARATOR); g_string_append (str, paths[i]); if (!g_file_test (str->str, G_FILE_TEST_IS_DIR)) mkdir (str->str, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); i++; } g_string_free (str, TRUE); g_strfreev (paths);}static char *get_type_name (char *filename){ char *ext; ext = strrchr (filename, '.'); if (!ext) return g_strdup (_("Unknown")); ext = g_utf8_strup (ext + 1, -1); if (strcmp (ext, "BMP") == 0) { g_free (ext); return g_strdup (_("Bitmap Image")); } else if (strcmp (ext, "JPG") == 0) { g_free (ext); return g_strdup (_("JPEG Image")); } else if (strcmp (ext, "GIF") == 0) { g_free (ext); return g_strdup (_("GIF Image")); } else if (strcmp (ext, "PNG") == 0) { g_free (ext); return g_strdup (_("PNG Image")); } else if (strcmp (ext, "TXT") == 0) { g_free (ext); return g_strdup (_("Text File")); } else if (strcmp (ext, "WAV") == 0) { g_free (ext); return g_strdup (_("Wave Sound")); } else if (strcmp (ext, "MP3") == 0) { g_free (ext); return g_strdup (_("MP3 Music")); } else if (strcmp (ext, "SPR") == 0) { g_free (ext); return g_strdup (_("Sprite Data")); } else if (strcmp (ext, "XML") == 0) { g_free (ext); return g_strdup (_("XML Document")); } else return ext;}/* Attempt to convert a string (possibly with Korean encoding) to UTF-8 */static inline char *str_to_utf8 (char *str, gsize *bytes_written){ char *encodings[] = { "CSEUCKR", "CSISO2022KR", "EUC-KR", "EUCKR", "ISO-2022-KR", "ISO646-KR", "ISO2022KR", "ISO8859-1", "UTF-8" }; int j; char *ret = NULL; if (!(ret = g_locale_to_utf8 (str, -1, NULL, bytes_written, NULL))) for (j = 0; j < sizeof (encodings) / sizeof (char *); j++) { ret = g_convert (str, -1, "UTF-8", encodings[j], NULL, bytes_written, NULL); if (ret) break; } return ret;}static char *friendly_size_name (unsigned long size){ if (size < 1024) return g_strdup_printf ("%ld bytes", size); else if (size >= 1024 && size < 1024 * 1024) return g_strdup_printf ("%.1f KB", size / 1024.0); else return g_strdup_printf ("%.1f MB", size / 1024.0 / 1024.0);}static unsigned longfill_filelist (){ long i; unsigned long num = 0; GtkTreeIter iter = {}; gchar *search; GPatternSpec *pattern = NULL; filling = TRUE; gtk_list_store_clear (GTK_LIST_STORE (filelist)); search = (gchar *) gtk_entry_get_text (GTK_ENTRY (W(searchentry))); if (search && *search) { if (!strchr (search, '*')) { search = g_strdup_printf ("*%s*", search); pattern = g_pattern_spec_new (search); g_free (search); } else pattern = g_pattern_spec_new (search); } /* Detach list model from view to make insertion faster */ g_object_ref (filelist); gtk_tree_view_set_model (GTK_TREE_VIEW (W(filelist)), NULL); /* We add items to the list in reversed order because for some reason the list reverses the order again. i is not an unsigned long because it will conflict with 'i >= 0' and 'i--' */ for (i = (long) grf->nfiles - 1; i >= 0; i--) { char *filename = NULL; char *size = NULL; char *type; if (!grf->files[i].real_len) continue; /* Do not display folders */ if (grf->files[i].type == 2) continue; /* Attempt to convert the filename to UTF-8 */ if (!grf->files[i].name) { printf("%ld: %s\n", i, grf->files[i].name); continue; } filename = str_to_utf8 (grf->files[i].name, NULL); if (!filename) continue; if (pattern && !g_pattern_match_string (pattern, filename)) { g_free (filename); continue; } size = friendly_size_name (grf->files[i].real_len); type = get_type_name (filename); /* Add to list */ gtk_list_store_prepend (GTK_LIST_STORE (filelist), &iter); gtk_list_store_set (GTK_LIST_STORE (filelist), &iter, INDEX_COL, i, DISPLAY_COL, filename, TYPE_COL, type, SIZE_COL, grf->files[i].real_len, SIZE_DISPLAY_COL, size, -1); num++; g_free (size); g_free (filename); g_free (type); } /* Re-attach model */ gtk_tree_view_set_model (GTK_TREE_VIEW (W(filelist)), filelist); g_object_unref (filelist); filling = FALSE; if (pattern) g_pattern_spec_free (pattern); return num;}static voidopen_grf_file (const char *fname){ char *title, *tmp; GrfError err; Grf *newgrf; GList *list, *cols; if (!g_file_test (fname, G_FILE_TEST_EXISTS)) { show_error (_("File %s does not exist."), fname); return; } gdk_window_set_cursor (W(main)->window, busy_cursor); set_status (_("Loading...")); while (gtk_events_pending ()) gtk_main_iteration (); newgrf = grf_open (fname, &err); if (!newgrf) { char *base; base = g_path_get_basename (fname); set_status (""); show_error (_("Error while opening %s:\n%s"), base, grf_strerror (err)); g_free (base); return; } if (grf) grf_free (grf); grf = newgrf; g_string_printf (filename, "%s", fname); title = g_strdup_printf (_("%s - GRF Tool"), fname); gtk_window_set_title (GTK_WINDOW (W(main)), title); g_free (title); gtk_list_store_clear (GTK_LIST_STORE (filelist)); cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (W(filelist))); for (list = cols; list; list = list->next) { GtkTreeViewColumn *col = (GtkTreeViewColumn *) list->data; gtk_tree_view_column_set_sort_indicator (col, FALSE); } gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (filelist), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING); g_list_free (cols); fill_filelist (); tmp = g_path_get_basename (fname); title = g_strdup_printf (_("%s: %ld files"), tmp, grf->nfiles); set_status (title); g_free (tmp); g_free (title); gtk_widget_set_sensitive (W(extract), TRUE); gdk_window_set_cursor (W(main)->window, NULL);}static gbooleanidle_open (gpointer fname){ open_grf_file ((const char *) fname); return FALSE;}static voidpreview_file (char *display, char *fname){ GtkTextBuffer *buf; char *ext; if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (W(preview_toggle)))) return; ext = strrchr (display, '.'); if (!ext) return; ext = g_utf8_strup (ext, -1); buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (W(text_preview))); gtk_text_buffer_set_text (buf, "", 0); gtk_image_set_from_file (GTK_IMAGE (W(image_preview)), NULL); if (strcmp (ext, ".TXT") == 0 || strcmp (ext, ".XML") == 0) { char *data, *text; unsigned long size; gsize strsize; GrfError err; (void *) data = grf_get (grf, fname, &size, &err); if (!data) { set_status (grf_strerror (err)); goto end; } text = str_to_utf8 (data, &strsize); size = strsize; g_free (data); gtk_text_buffer_set_text (buf, text, size); gtk_notebook_set_current_page (GTK_NOTEBOOK (W(notebook1)), 0); g_free (text); } else if (strcmp (ext, ".BMP") == 0 || strcmp (ext, ".JPG") == 0 || strcmp (ext, ".PNG") == 0 || strcmp (ext, ".GIF") == 0) { char *data, *tmpfile = NULL; unsigned long size; GrfError err; GError *gerr = NULL; int fd; (void *) data = grf_get (grf, fname, &size, &err); if (!data) { set_status (grf_strerror (err)); goto end; } if ((fd = g_file_open_tmp ("grftoolXXXXXX", &tmpfile, &gerr)) == -1) { show_error (_("Unable to create a temporary file: %s"), gerr->message); g_error_free (gerr); goto end; } write (fd, data, size); close (fd); gtk_image_set_from_file (GTK_IMAGE (W(image_preview)), tmpfile); remove (tmpfile); g_free (tmpfile); gtk_notebook_set_current_page (GTK_NOTEBOOK (W(notebook1)), 1); } else gtk_notebook_set_current_page (GTK_NOTEBOOK (W(notebook1)), 0); end: g_free (ext);}static gpointerextract_thread (gpointer user_data){ GList *args = user_data; const char *savedir = g_list_nth_data (args, 0); GList *files = g_list_nth_data (args, 1); GList *indices = g_list_nth_data (args, 2); GList *l, *f, *dirs = NULL; char *dir; unsigned long failed = 0, max = 0; gboolean stop = FALSE; GTimer *timer = NULL; g_list_free (args); /* Generate a list of unique subdirectories */ for (l = files; l; l = l->next) { char *p; /* Convert paths to Unix paths */ for (p = (char *) l->data; *p; p++) if (*p == '\\') *p = G_DIR_SEPARATOR; dir = g_path_get_dirname ((gchar *) l->data); if (!g_list_find_custom (dirs, dir, (GCompareFunc) g_ascii_strcasecmp)) { dirs = g_list_prepend (dirs, dir); max++; } else g_free (dir); } g_static_mutex_lock (&extractProgressM); extractProgress.max = max; extractProgress.status = STATUS_MKDIR; g_static_mutex_unlock (&extractProgressM); /* Create the subdirectories */ dirs = g_list_sort (dirs, (GCompareFunc) g_ascii_strcasecmp); dirs = g_list_reverse (dirs); for (l = dirs; l; l = l->next) { dir = g_build_filename (G_DIR_SEPARATOR_S, savedir, (char *) l->data, NULL); mkdirs (dir); g_free (dir); g_static_mutex_lock (&extractProgressM); extractProgress.current++; stop = extractProgress.stop; g_static_mutex_unlock (&extractProgressM); } g_list_foreach (dirs, (GFunc) g_free, NULL); g_list_free (dirs); if (stop) goto end; g_static_mutex_lock (&extractProgressM); extractProgress.current = 0; extractProgress.max = g_list_length (files); extractProgress.status = STATUS_EXTRACT; stop = extractProgress.stop; g_static_mutex_unlock (&extractProgressM); if (stop) goto end; timer = g_timer_new (); g_timer_start (timer); /* Start the actual extraction */ for (l = indices, f = files; l; l = l->next, f = f->next) { unsigned long i; char *fname; i = GPOINTER_TO_INT (l->data); fname = g_build_filename (savedir, (char *) f->data, NULL); if (!grf_index_extract (grf, i, fname, NULL)) failed++; g_free (fname); g_static_mutex_lock (&extractProgressM); extractProgress.current++; strncpy (extractProgress.file, grf->files[i].name, PATH_MAX - 1); stop = extractProgress.stop;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -