1 /** \file   crtpreviewwidget.c
2  * \brief   Widget to show CRT cart image data before attaching
3  *
4  * \author  Bas Wassink <b.wassink@ziggo.nl>
5  */
6 
7 /*
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #include <gtk/gtk.h>
31 
32 #include "basewidgets.h"
33 #include "c64/cart/crt.h"
34 #include "cartridge.h"
35 #include "debug_gtk3.h"
36 #include "machine.h"
37 #include "widgethelpers.h"
38 
39 #include "crtpreviewwidget.h"
40 
41 
42 /** \brief  CHIP packet type descriptions
43  *
44  * Used for column 0
45  */
46 static const gchar *packet_type[4] = {
47     "ROM", "RAM", "Flash", "<invalid>"
48 };
49 
50 
51 /** \brief  Game/Exrom line state descriptions
52  */
53 static const gchar *romstate[2] = {
54     "active (lo)", "inactive (hi)"
55 };
56 
57 /** \brief  Open function */
58 static FILE *(*open_func)(const char *, crt_header_t *header) = NULL;
59 /** \brief  Function to read chip header */
60 static int (*chip_func)(crt_chip_header_t *, FILE *) = NULL;
61 
62 /* FIXME:   Do we actually need all these references? Perhaps get them via
63  *          gtk_grid_get_child_at() ?
64  */
65 
66 /** \brief  CRT ID label */
67 static GtkWidget *crtid_label = NULL;
68 /** \brief  CRT revision label */
69 static GtkWidget *crtrevision_label = NULL;
70 /** 'brief  CRT name label */
71 static GtkWidget *crtname_label = NULL;
72 /** \brief  EX ROM label */
73 static GtkWidget *exrom_label = NULL;
74 /** \brief  Game title label */
75 static GtkWidget *game_label = NULL;
76 /** \brief  GtkTreeView used for the CRT data display */
77 static GtkWidget *chip_tree = NULL;
78 
79 
80 /** \brief  Renderer handler to print CHIP packet load address as hex
81  *
82  * \param[in]       column      tree view column
83  * \param[in,out]   renderer    cell renderer
84  * \param[in]       model       tree model
85  * \param[in]       iter        tree iterator
86  * \param[in]       user_data   extra event data (unused)
87  */
load_to_hex(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer userdata)88 static void load_to_hex(GtkTreeViewColumn *column,
89                         GtkCellRenderer *renderer,
90                         GtkTreeModel *model,
91                         GtkTreeIter *iter,
92                         gpointer userdata)
93 {
94     gchar buffer[0x10];
95     guint value;
96 
97     gtk_tree_model_get(model, iter, 1, &value, -1);
98     g_snprintf(buffer, sizeof(buffer), "$%04X", value);
99     g_object_set(renderer, "text", buffer, NULL);
100 }
101 
102 
103 /** \brief  Renderer handler to print CHIP packet size as hex
104  *
105  * \param[in]       column      tree view column
106  * \param[in,out]   renderer    cell renderer
107  * \param[in]       model       tree model
108  * \param[in]       iter        tree iterator
109  * \param[in]       user_data   extra event data (unused)
110  */
size_to_hex(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer userdata)111 static void size_to_hex(GtkTreeViewColumn *column,
112                         GtkCellRenderer *renderer,
113                         GtkTreeModel *model,
114                         GtkTreeIter *iter,
115                         gpointer userdata)
116 {
117     gchar buffer[0x10];
118     guint value;
119 
120     gtk_tree_model_get(model, iter, 2, &value, -1);
121     g_snprintf(buffer, sizeof(buffer), "$%04X", value);
122     g_object_set(renderer, "text", buffer, NULL);
123 }
124 
125 
126 /** \brief  Create left-indented label
127  *
128  * \param[in]   s   text for label
129  *
130  * \return  GtkLabel
131  */
create_label(const char * s)132 static GtkWidget *create_label(const char *s)
133 {
134     GtkWidget *label = gtk_label_new(s);
135     gtk_widget_set_halign(label, GTK_ALIGN_START);
136     return label;
137 }
138 
139 
140 /** \brief  Create CHIP packets view and model
141  *
142  * \return  GtkTreeView
143  */
create_tree_view(void)144 static GtkWidget *create_tree_view(void)
145 {
146     GtkListStore *model;
147     GtkWidget *view;
148     GtkTreeViewColumn *col_type;
149     GtkTreeViewColumn *col_load;
150     GtkTreeViewColumn *col_size;
151     GtkTreeViewColumn *col_bank;
152 
153     GtkCellRenderer *renderer;
154 
155     /* set up model */
156     model = gtk_list_store_new(4,
157             G_TYPE_STRING,  /* packet type ('RAM', 'ROM', 'Flash') */
158             G_TYPE_UINT,   /* load address */
159             G_TYPE_UINT,    /* size */
160             G_TYPE_UINT     /* bank */
161             );
162 
163 
164     /* set up view */
165     view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
166     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
167 
168     /* create standard renderer (also handles int/float despite its name */
169     renderer = gtk_cell_renderer_text_new();
170 
171     /* type */
172     col_type = gtk_tree_view_column_new_with_attributes(
173             "type", renderer, "text", 0, NULL);
174 
175     /* load */
176     col_load = gtk_tree_view_column_new_with_attributes(
177             "load", renderer, "text", 1, NULL);
178     /* set HEX format handler */
179     gtk_tree_view_column_set_cell_data_func(col_load, renderer, load_to_hex,
180             NULL, NULL);
181 
182 
183     /* size */
184     col_size = gtk_tree_view_column_new_with_attributes(
185             "size", renderer, "text", 2, NULL);
186     /* set HEX format handler */
187     gtk_tree_view_column_set_cell_data_func(col_size, renderer, size_to_hex,
188             NULL, NULL);
189 
190     /* bank */
191     col_bank = gtk_tree_view_column_new_with_attributes(
192             "bank", renderer, "text", 3, NULL);
193 
194     gtk_tree_view_append_column(GTK_TREE_VIEW(view), col_type);
195     gtk_tree_view_append_column(GTK_TREE_VIEW(view), col_load);
196     gtk_tree_view_append_column(GTK_TREE_VIEW(view), col_size);
197     gtk_tree_view_append_column(GTK_TREE_VIEW(view), col_bank);
198 
199     gtk_widget_show(view);
200     return view;
201 }
202 
203 
204 /** \brief  Delete all rows from the model
205  */
chip_packets_clear(void)206 static void chip_packets_clear(void)
207 {
208     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(chip_tree));
209     gtk_list_store_clear(GTK_LIST_STORE(model));
210 }
211 
212 
213 /** \brief  Add a row to the CHIP packet table
214  *
215  * \param[in]   type    packet type
216  * \param[in]   load    load address
217  * \param[in]   size    packet size
218  * \param[in]   bank    packet bank index
219  */
chip_packet_add(uint16_t type,uint16_t load,uint16_t size,uint16_t bank)220 static void chip_packet_add(uint16_t type,
221                             uint16_t load,
222                             uint16_t size,
223                             uint16_t bank)
224 {
225     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(chip_tree));
226 
227     debug_gtk3("adding row: %u, %u, %u, %u.", type, load, size, bank);
228 
229     gtk_list_store_insert_with_values(GTK_LIST_STORE(model), NULL, -1,
230         0, packet_type[type & 0x03], 1, load, 2, size, 3, bank, -1);
231 }
232 
233 
234 /** \brief  Create cartridge preview widget
235  *
236  * \return  GtkGrid
237  */
crt_preview_widget_create(void)238 GtkWidget *crt_preview_widget_create(void)
239 {
240     GtkWidget *grid;
241     GtkWidget *label;
242     GtkWidget *scroll;
243     int row;
244 
245     grid = uihelpers_create_grid_with_label("CRT Header:", 2);
246     gtk_grid_set_column_spacing(GTK_GRID(grid), 16);
247     gtk_grid_set_row_spacing(GTK_GRID(grid), 8);
248     row = 1;
249 
250     label = create_label("ID:");
251     crtid_label = create_label("<unknown>");
252     gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
253     gtk_grid_attach(GTK_GRID(grid), crtid_label, 1, row, 1, 1);
254     row++;
255 
256     label = create_label("Revision:");
257     crtrevision_label = create_label("<unknown>");
258     gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
259     gtk_grid_attach(GTK_GRID(grid), crtrevision_label, 1, row, 1, 1);
260     row++;
261 
262     label = create_label("Name:");
263     crtname_label = create_label("<unknown>");
264     gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
265     gtk_grid_attach(GTK_GRID(grid), crtname_label, 1, row, 1, 1);
266     row++;
267 
268     label = create_label("EXROM:");
269     exrom_label = create_label("<unknown>");
270     gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
271     gtk_grid_attach(GTK_GRID(grid), exrom_label, 1, row, 1, 1);
272     row++;
273 
274     label = create_label("GAME:");
275     game_label = create_label("<unknown>");
276     gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
277     gtk_grid_attach(GTK_GRID(grid), game_label, 1, row, 1, 1);
278     row++;
279 
280     label = gtk_label_new(NULL);
281     gtk_widget_set_halign(label, GTK_ALIGN_START);
282     gtk_label_set_markup(GTK_LABEL(label), "<b>CHIP packets:</b>");
283     gtk_grid_attach(GTK_GRID(grid), label, 0, row, 2, 1);
284     row++;
285 
286     scroll = gtk_scrolled_window_new(NULL, NULL);
287     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
288             GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
289     gtk_widget_set_vexpand(scroll, TRUE);
290     chip_tree = create_tree_view();
291     gtk_widget_set_vexpand(chip_tree, TRUE);
292     gtk_container_add(GTK_CONTAINER(scroll), chip_tree);
293     gtk_grid_attach(GTK_GRID(grid), scroll, 0, row, 2, 1);
294 
295     gtk_widget_show_all(grid);
296     return grid;
297 }
298 
299 
300 /** \brief  Set function to open a CRT file and read its header
301  *
302  * Required to avoid linking errors with VSID
303  *
304  * \param[in]   func    crt_open() reference
305  */
crt_preview_widget_set_open_func(FILE * (* func)(const char *,crt_header_t *))306 void crt_preview_widget_set_open_func(FILE *(*func)(const char *, crt_header_t *))
307 {
308     open_func = func;
309 }
310 
311 
312 /** \brief  Set function to read a CRT CHIP packet
313  *
314  * Required to avoid linking errors with VSID
315  *
316  * \param[in]   func    crt_read_chip_header() reference
317  */
crt_preview_widget_set_chip_func(int (* func)(crt_chip_header_t *,FILE *))318 void crt_preview_widget_set_chip_func(int (*func)(crt_chip_header_t *, FILE *))
319 {
320     chip_func = func;
321 }
322 
323 
324 /** \brief  Update widget with data from CTR image \a path
325  *
326  * \param[in]   path    path to CRT file
327  */
crt_preview_widget_update(const gchar * path)328 void crt_preview_widget_update(const gchar *path)
329 {
330     FILE *fd;
331     crt_header_t header;
332     crt_chip_header_t chip;
333     gchar buffer[1024];
334 #if 0
335 # ifdef HAVE_DEBUG_GTK3UI
336     int packets = 0;
337 # endif
338 #endif
339 
340     /*
341      * Guard against non C64/C128 carts
342      * Once we implement CRT headers for VIC20 and others, this needs to be
343      * removed.
344      */
345     if (machine_class != VICE_MACHINE_C64
346             && machine_class != VICE_MACHINE_C64SC
347             && machine_class != VICE_MACHINE_C128)
348     {
349         debug_gtk3("Machine class != c64/c128, skipping.");
350         return;
351     }
352 
353     fd = open_func(path, &header);
354     if (fd == NULL) {
355         debug_gtk3("failed to open crt image");
356         gtk_label_set_text(GTK_LABEL(crtid_label), "<unknown>");
357         gtk_label_set_text(GTK_LABEL(crtrevision_label), "<unknown>");
358         gtk_label_set_text(GTK_LABEL(crtname_label), "<unknown>");
359         gtk_label_set_text(GTK_LABEL(exrom_label), "<unknown>");
360         gtk_label_set_text(GTK_LABEL(game_label), "<unknown>");
361         return;
362     }
363 
364     /* type */
365     g_snprintf(buffer, sizeof(buffer), "%d", (int)header.type);
366     gtk_label_set_text(GTK_LABEL(crtid_label), buffer);
367 
368     /* revision */
369     g_snprintf(buffer, sizeof(buffer), "%d", (int)header.subtype);
370     gtk_label_set_text(GTK_LABEL(crtrevision_label), buffer);
371 
372     /* name */
373     gtk_label_set_text(GTK_LABEL(crtname_label), header.name);
374 
375     /* exrom */
376     gtk_label_set_text(GTK_LABEL(exrom_label),
377             romstate[header.exrom ? 1 : 0]);
378 
379     /* game */
380     gtk_label_set_text(GTK_LABEL(game_label),
381             romstate[header.game ? 1 : 0]);
382 
383     /* clear CHIP packet table */
384     chip_packets_clear();
385 
386 
387     while (1) {
388         long int pos;
389         uint32_t skip;
390 #if 0
391         debug_gtk3("reading packet #%d.", packets++);
392 #endif
393         if (chip_func(&chip, fd) != 0) {
394             debug_gtk3("couldn't read further CHIP packets, exiting.");
395             break;
396         }
397         skip = chip.size;
398 
399         debug_gtk3("chip packet contents:");
400         debug_gtk3("    skip = %lu", (long unsigned)chip.skip);
401         debug_gtk3("    load = %u", chip.start);
402         debug_gtk3("    size = %u", chip.size);
403         debug_gtk3("    bank = %u", chip.bank);
404 
405         chip_packet_add(chip.type, chip.start, chip.size, chip.bank);
406 
407         pos = ftell(fd) + skip;
408 #if 0
409         debug_gtk3("next chip packet offset = %lx", (unsigned long)pos);
410 #endif
411         if (fseek(fd, pos, SEEK_SET) != 0) {
412             debug_gtk3("OEPS!");
413             break;
414         }
415     }
416 #if 0
417     debug_gtk3("read %d CHIP packets.", packets);
418 #endif
419     fclose(fd);
420 }
421