1 /*
2  * TilEm II
3  *
4  * Copyright (c) 2010-2011 Thibault Duponchelle
5  * Copyright (c) 2010-2012 Benjamin Moody
6  *
7  * This program is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <gtk/gtk.h>
28 #include <glib.h>
29 #include <ticalcs.h>
30 #include <tilem.h>
31 
32 #include "gui.h"
33 #include "files.h"
34 #include "icons.h"
35 #include "msgbox.h"
36 
37 /* CMD LINE OPTIONS */
38 static gchar* cl_romfile = NULL;
39 static gchar* cl_skinfile = NULL;
40 static gchar* cl_model = NULL;
41 static gchar* cl_statefile = NULL;
42 static gchar** cl_files_to_load = NULL;
43 static gboolean cl_skinless_flag = FALSE;
44 static gboolean cl_reset_flag = FALSE;
45 static gchar* cl_getvar = NULL;
46 static gchar* cl_macro_to_run = NULL;
47 static gboolean cl_debug_flag = FALSE;
48 static gboolean cl_normalspeed_flag = FALSE;
49 static gboolean cl_fullspeed_flag = FALSE;
50 
51 
52 static GOptionEntry entries[] =
53 {
54 	{ "rom", 'r', 0, G_OPTION_ARG_FILENAME, &cl_romfile, "The rom file to run", "FILE" },
55 	{ "skin", 'k', 0, G_OPTION_ARG_FILENAME, &cl_skinfile, "The skin file to use", "FILE" },
56 	{ "model", 'm', 0, G_OPTION_ARG_STRING, &cl_model, "The model to use", "NAME" },
57 	{ "state-file", 's', 0, G_OPTION_ARG_FILENAME, &cl_statefile, "The state-file to use", "FILE" },
58 	{ "without-skin", 'l', 0, G_OPTION_ARG_NONE, &cl_skinless_flag, "Start in skinless mode", NULL },
59 	{ "reset", 0, 0, G_OPTION_ARG_NONE, &cl_reset_flag, "Reset the calc at startup", NULL },
60 	{ "get-var", 0, 0, G_OPTION_ARG_STRING, &cl_getvar, "Get a var at startup", "FILE" },
61 	{ "play-macro", 'p', 0, G_OPTION_ARG_FILENAME, &cl_macro_to_run, "Run this macro at startup", "FILE" },
62 	{ "debug", 'd', 0, G_OPTION_ARG_NONE, &cl_debug_flag, "Launch debugger", NULL },
63 	{ "normal-speed", 0, 0, G_OPTION_ARG_NONE, &cl_normalspeed_flag, "Run at normal speed", NULL },
64 	{ "full-speed", 0, 0, G_OPTION_ARG_NONE, &cl_fullspeed_flag, "Run at maximum speed", NULL },
65 	{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &cl_files_to_load, NULL, "FILE" },
66 	{ 0, 0, 0, 0, 0, 0, 0 }
67 };
68 
69 
70 /* #########  MAIN  ######### */
71 
72 /* Order of preference for automatic model selection. */
73 static const char model_search_order[] =
74 	{ TILEM_CALC_TI81,
75 	  TILEM_CALC_TI73,
76 	  TILEM_CALC_TI82,
77 	  TILEM_CALC_TI83,
78 	  TILEM_CALC_TI76,
79 	  TILEM_CALC_TI84P_SE,
80 	  TILEM_CALC_TI84P,
81 	  TILEM_CALC_TI83P_SE,
82 	  TILEM_CALC_TI83P,
83 	  TILEM_CALC_TI84P_NSPIRE,
84 	  TILEM_CALC_TI85,
85 	  TILEM_CALC_TI86, 0 };
86 
87 /* Check if given calc model should be used for these file types. */
check_file_types(int calc_model,const int * file_models,int nfiles)88 static gboolean check_file_types(int calc_model,
89                                  const int *file_models,
90                                  int nfiles)
91 {
92 	/* Only choose a calc model if it supports all of the given
93 	   file types, and at least one of the files is of the calc's
94 	   "preferred" type.  This means if we have a mixture of 82Ps
95 	   and 83Ps, we can use either a TI-83 or TI-76.fr ROM image,
96 	   but not a TI-83 Plus. */
97 
98 	gboolean preferred = FALSE;
99 	int i;
100 
101 	calc_model = model_to_base_model(calc_model);
102 
103 	for (i = 0; i < nfiles; i++) {
104 		if (file_models[i] == calc_model)
105 			preferred = TRUE;
106 		else if (!model_supports_file(calc_model, file_models[i]))
107 			return FALSE;
108 	}
109 
110 	return preferred;
111 }
112 
load_initial_rom(TilemCalcEmulator * emu,const char * cmdline_rom_name,const char * cmdline_state_name,char ** cmdline_files,int model)113 static void load_initial_rom(TilemCalcEmulator *emu,
114                              const char *cmdline_rom_name,
115                              const char *cmdline_state_name,
116                              char **cmdline_files,
117                              int model)
118 {
119 	GError *err = NULL;
120 	char *modelname;
121 	int nfiles, *file_models, i;
122 
123 	/* If a ROM file is specified on the command line, use that
124 	   (and no other) */
125 
126 	if (cmdline_rom_name) {
127 		if (tilem_calc_emulator_load_state(emu, cmdline_rom_name,
128 		                                   cmdline_state_name,
129 		                                   model, &err))
130 			return;
131 		else if (!err)
132 			exit(0);
133 		else {
134 			g_printerr("%s\n", err->message);
135 			exit(1);
136 		}
137 	}
138 
139 	/* Choose model by file names */
140 
141 	if (!model && cmdline_files) {
142 		nfiles = g_strv_length(cmdline_files);
143 		file_models = g_new(int, nfiles);
144 
145 		/* determine model for each filename */
146 		for (i = 0; i < nfiles; i++)
147 			file_models[i] = file_to_model(cmdline_files[i]);
148 
149 		/* iterate over all known models... */
150 		for (i = 0; model_search_order[i]; i++) {
151 			model = model_search_order[i];
152 
153 			/* check if this model supports the named files */
154 			if (!check_file_types(model, file_models, nfiles))
155 				continue;
156 
157 			/* try to load model, but no error message if
158 			   no ROM is present in config */
159 			if (tilem_calc_emulator_load_state(emu, NULL, NULL,
160 			                                   model, &err)) {
161 				g_free(file_models);
162 				return;
163 			}
164 			else if (!err)
165 				exit(0);
166 			else if (!g_error_matches(err, TILEM_EMULATOR_ERROR,
167 			                          TILEM_EMULATOR_ERROR_NO_ROM)) {
168 				messagebox01(NULL, GTK_MESSAGE_ERROR,
169 				             "Unable to load calculator state",
170 				             "%s", err->message);
171 			}
172 			g_clear_error(&err);
173 		}
174 
175 		g_free(file_models);
176 		model = 0;
177 	}
178 
179 	/* If no model specified on command line (either explicitly or
180 	   implicitly), then choose the most recently used model */
181 
182 	if (!model && !cmdline_files) {
183 		tilem_config_get("recent", "last_model/s", &modelname, NULL);
184 		if (modelname)
185 			model = name_to_model(modelname);
186 	}
187 
188 	/* Try to load the most recently used ROM for chosen model */
189 
190 	if (model) {
191 		if (tilem_calc_emulator_load_state(emu, NULL, NULL,
192 		                                   model, &err))
193 			return;
194 		else if (!err)
195 			exit(0);
196 		else {
197 			messagebox01(NULL, GTK_MESSAGE_ERROR,
198 			             "Unable to load calculator state",
199 			             "%s", err->message);
200 			g_clear_error(&err);
201 		}
202 	}
203 
204 	/* Prompt user for a ROM file */
205 
206 	while (!emu->calc) {
207 		if (!tilem_calc_emulator_prompt_open_rom(emu))
208 			exit(0);
209 	}
210 }
211 
main(int argc,char ** argv)212 int main(int argc, char **argv)
213 {
214 	TilemCalcEmulator* emu;
215 	char *menurc_path;
216 	GOptionContext *context;
217 	GError *error = NULL;
218 	int model = 0;
219 
220 	g_thread_init(NULL);
221 	gtk_init(&argc, &argv);
222 	set_program_path(argv[0]);
223 	g_set_application_name("TilEm");
224 
225 	menurc_path = get_shared_file_path("menurc", NULL);
226 	if (menurc_path)
227 		gtk_accel_map_load(menurc_path);
228 	g_free(menurc_path);
229 
230 	init_custom_icons();
231 	gtk_window_set_default_icon_name("tilem");
232 
233 	emu = tilem_calc_emulator_new();
234 
235 	context = g_option_context_new(NULL);
236 	g_option_context_add_main_entries(context, entries, NULL);
237 	g_option_context_add_group(context, gtk_get_option_group(TRUE));
238 	if (!g_option_context_parse(context, &argc, &argv, &error))
239 	{
240 		g_printerr("%s: %s\n", g_get_prgname(), error->message);
241 		exit (1);
242 	}
243 
244 	if (cl_model) {
245 		model = name_to_model(cl_model);
246 		if (!model) {
247 			g_printerr("%s: unknown model %s\n",
248 			           g_get_prgname(), cl_model);
249 			return 1;
250 		}
251 	}
252 
253 	load_initial_rom(emu, cl_romfile, cl_statefile, cl_files_to_load, model);
254 
255 	emu->ewin = tilem_emulator_window_new(emu);
256 
257 	if (cl_skinless_flag)
258 		tilem_emulator_window_set_skin_disabled(emu->ewin, TRUE);
259 	else if (cl_skinfile) {
260 		tilem_emulator_window_set_skin(emu->ewin, cl_skinfile);
261 		tilem_emulator_window_set_skin_disabled(emu->ewin, FALSE);
262 	}
263 
264 	gtk_widget_show(emu->ewin->window);
265 
266 	ticables_library_init();
267 	tifiles_library_init();
268 	ticalcs_library_init();
269 
270 	if (cl_reset_flag)
271 		tilem_calc_emulator_reset(emu);
272 
273 	if (cl_fullspeed_flag)
274 		tilem_calc_emulator_set_limit_speed(emu, FALSE);
275 	else if (cl_normalspeed_flag)
276 		tilem_calc_emulator_set_limit_speed(emu, TRUE);
277 
278 	if (cl_files_to_load)
279 		load_files_cmdline(emu->ewin, cl_files_to_load);
280 	if (cl_macro_to_run)
281 		tilem_macro_load(emu, cl_macro_to_run);
282 	if (cl_getvar)
283 		tilem_link_receive_matching(emu, cl_getvar, ".");
284 
285 	if (cl_debug_flag)
286 		launch_debugger(emu->ewin);
287 	else
288 		tilem_calc_emulator_run(emu);
289 
290 	g_signal_connect(emu->ewin->window, "destroy",
291 	                 G_CALLBACK(gtk_main_quit), NULL);
292 
293 	gtk_main();
294 
295 	tilem_calc_emulator_pause(emu);
296 
297 	tilem_emulator_window_free(emu->ewin);
298 	tilem_calc_emulator_free(emu);
299 
300 	menurc_path = get_config_file_path("menurc", NULL);
301 	gtk_accel_map_save(menurc_path);
302 	g_free(menurc_path);
303 
304 	ticables_library_exit();
305 	tifiles_library_exit();
306 	ticalcs_library_exit();
307 
308 	return 0;
309 }
310