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