1 /*
2  * TilEm II
3  *
4  * Copyright (c) 2010-2011 Thibault Duponchelle
5  *
6  * This program is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include <stdlib.h>
25 #include <string.h>
26 #include <gtk/gtk.h>
27 #include <glib/gstdio.h>
28 #include <ticalcs.h>
29 #include <tilem.h>
30 
31 #include "gui.h"
32 #include "filedlg.h"
33 #include "emucore.h"
34 
35 
36 /* Allocate a new TilemMacro structure which is empty */
tilem_macro_new()37 static TilemMacro* tilem_macro_new() {
38 	TilemMacro *macro = g_new(TilemMacro, 1);
39 	macro->n = 0;
40 	macro->actions = NULL;
41 	return macro;
42 }
43 
44 /* New or renew the table of actions (each TilemMacroAtom is an action) */
tilem_macro_actions_new(TilemMacro * macro,int n)45 static TilemMacroAtom** tilem_macro_actions_new(TilemMacro *macro, int n) {
46 	TilemMacroAtom **atom;
47 
48 	if(n == 0) {
49 		atom =  g_new(TilemMacroAtom*, n);
50 	} else {
51 		atom =  g_renew(TilemMacroAtom*, macro->actions, n);
52 	}
53 	return atom;
54 }
55 
56 /* Try to destroy the TilemMacro if really allocated */
tilem_macro_finalize(TilemMacro * macro)57 static void tilem_macro_finalize(TilemMacro* macro) {
58 	if(macro) {
59 		if(macro->actions)
60 			g_free(macro->actions);
61 		g_free(macro);
62 	}
63 }
64 
65 /* Firstly free the memory then create a new TilemMacro */
tilem_macro_start(TilemCalcEmulator * emu)66 void tilem_macro_start(TilemCalcEmulator *emu) {
67 	emu->isMacroRecording = TRUE;
68 
69 	/* Firstly destroy the macro if exists */
70 	tilem_macro_finalize(emu->macro);
71 
72 	/* Then allocate a new one */
73 	emu->macro = tilem_macro_new(emu);
74 }
75 
76 /* Add an action to the macro. The action could be :
77  * A keypress (type == 0)
78  * A file load (type == 1)
79  * Or something else if I implement it :)
80  */
tilem_macro_add_action(TilemMacro * macro,int type,char * value)81 void tilem_macro_add_action(TilemMacro* macro, int type, char * value) {
82 
83 	int n = macro->n;
84 
85 	/* We want to allocate for 1 object, but index is 0 */
86 	macro->actions = tilem_macro_actions_new(macro, n + 1);
87 
88 	/* Then we need to save the action */
89 	macro->actions[n] =  g_new(char, strlen(value)); /* FIXME : gcc says : "assignment from incompatible pointer type" ??? */
90 	macro->actions[n]->value = g_strdup(value);
91 	macro->actions[n]->type = type;
92 	macro->n++;
93 }
94 
95 /* Stop the macro */
tilem_macro_stop(TilemCalcEmulator * emu)96 void tilem_macro_stop(TilemCalcEmulator *emu)
97 {
98 	if(emu->isMacroRecording)
99 		emu->isMacroRecording = FALSE;
100 }
101 
102 /* Print the macro actions content (debug) */
tilem_macro_print(TilemMacro * macro)103 void tilem_macro_print(TilemMacro *macro) {
104 	int i = 0;
105 
106 	printf("macro->n : %d\n", macro->n);
107 	for(i = 0; i < macro->n; i++ ){
108 		printf("type : %d    value : %s\n", macro->actions[i]->type, macro->actions[i]->value);
109 	}
110 }
111 
112 /* Write a file using TilemMacro structure */
tilem_macro_write_file(TilemCalcEmulator * emu)113 void tilem_macro_write_file(TilemCalcEmulator *emu) {
114 	char *dir, *filename;
115 	tilem_config_get("macro",
116                  "directory/f", &dir,
117                  NULL);
118 
119 	filename = prompt_save_file("Save macro",
120 				    GTK_WINDOW(emu->ewin->window),
121 				    NULL,
122 				    dir,
123 				    "Macro files", "*.txt",
124                                     "All files", "*",
125 				    NULL);
126 	if(filename) {
127 		FILE * fp = g_fopen(filename, "w");
128 		if(fp) {
129 			int i = 0;
130 			for(i = 0; i< emu->macro->n; i++ ){
131 				printf("type : %d    value : %s\n", emu->macro->actions[i]->type, emu->macro->actions[i]->value);
132 				/* Test if it's a key press or a file loading action */
133 				if(emu->macro->actions[i]->type == 1) {
134 					char * lengthchar = g_new0(char, 4);
135 					int length = strlen(emu->macro->actions[i]->value);
136 					fwrite("file=", 1, 5, fp);
137 					sprintf(lengthchar, "%04d", strlen(emu->macro->actions[i]->value));
138 					fwrite(lengthchar, 1, sizeof(int), fp);
139 					fwrite("-", 1, 1, fp);
140 					fwrite(emu->macro->actions[i]->value, 1, length, fp);
141 					g_free(lengthchar);
142 				} else {
143 					fwrite(emu->macro->actions[i]->value, 1, 4, fp);
144 					fwrite(",", 1, 1, fp);
145 				}
146 			}
147 			tilem_config_set("macro", "directory/f", g_path_get_dirname(filename), NULL);
148 			fclose(fp);
149 		}
150 		g_free(filename);
151 		g_free(dir);
152 	}
153 }
154 
155 #define MACRO_KEYPRESS 0
156 #define MACRO_FILE 1
157 
158 /* Play the macro (macro should be created or loaded before else it does nothing) */
tilem_macro_play_main(TilemCalcEmulator * emu,G_GNUC_UNUSED gpointer data)159 static gboolean tilem_macro_play_main(TilemCalcEmulator *emu, G_GNUC_UNUSED gpointer data) {
160 
161 	if(!emu->macro) {
162 		printf("Nothing to play\n");
163 		return FALSE;
164 	}
165 
166 	int i;
167 	for(i = 0; i < emu->macro->n; i++ ){
168 		if(emu->macro->actions[i]->type == MACRO_FILE) {
169 			/* Type == 1 is load file */
170 			struct TilemSendFileInfo *sf;
171 			sf = g_slice_new0(struct TilemSendFileInfo);
172 			sf->filename = g_strdup(emu->macro->actions[i]->value);
173 			sf->display_name = g_filename_display_basename(emu->macro->actions[i]->value);
174 			sf->slot = -1;
175 			sf->first = TRUE;
176 			sf->last = TRUE;
177 			send_file_main(emu, sf);
178 
179 		} else {
180 			/* type == 0 is keypress */
181 			int code = atoi(emu->macro->actions[i]->value);
182 			tilem_em_unlock(emu);
183 			run_with_key_slowly(emu->calc, code);
184 			tilem_em_lock(emu);
185 
186 		}
187 	}
188 
189 
190 	return TRUE;
191 }
192 
193 
tilem_macro_play_finished(G_GNUC_UNUSED TilemCalcEmulator * emu,G_GNUC_UNUSED gpointer data,G_GNUC_UNUSED gboolean cancelled)194 static void tilem_macro_play_finished(G_GNUC_UNUSED TilemCalcEmulator *emu, G_GNUC_UNUSED gpointer data,
195                                G_GNUC_UNUSED gboolean cancelled) {
196 
197 }
198 
199 /* Play the macro */
tilem_macro_play(TilemCalcEmulator * emu)200 void tilem_macro_play(TilemCalcEmulator* emu) {
201 
202 	tilem_calc_emulator_begin(emu, &tilem_macro_play_main, &tilem_macro_play_finished, NULL);
203 }
204 
205 /* Load a macro (when finished, task manager will normally call tilem_macro_play) */
tilem_macro_load_main(TilemCalcEmulator * emu,gpointer data)206 static gboolean tilem_macro_load_main(TilemCalcEmulator* emu, gpointer data) {
207 
208 	char* filename = (char*) data;
209 	char c = 'a';
210 
211 	if(filename) {
212 		FILE * fp = g_fopen(filename, "r");
213 		/* printf("filename : %s\n", filename); */
214 
215 		tilem_macro_start(emu);
216 		while(c != EOF) {
217 			char* codechar = g_new0(char, 4);
218 			fread(codechar, 1, 4, fp);
219 			if(strcmp(codechar, "file") == 0) {
220 				c = fgetc(fp); /* Drop the "="*/
221 				char *lengthchar = g_new0(char, 4);
222 				fread(lengthchar, 1, 4, fp);
223 				c = fgetc(fp); /* Drop the "-"*/
224 				int length = atoi(lengthchar);
225 				char* filetoload= g_new0(char, length);
226 				fread(filetoload, 1, length, fp);
227 				tilem_macro_add_action(emu->macro, 1, filetoload);
228 				g_free(lengthchar);
229 				g_free(filetoload);
230 			} else {
231 				int code = atoi(codechar);
232 				if(code <= 0)
233 					break;
234 				/*printf("code : %d, codechar : %s\n",code,  codechar); */
235 				tilem_macro_add_action(emu->macro, 0, codechar);
236 				c = fgetc(fp);
237 			}
238 		}
239 		tilem_macro_stop(emu);
240 		fclose(fp);
241 	}
242 
243 	return TRUE;
244 }
245 
246 /* When the macro is totally loaded, then we can play it ! */
tilem_macro_load_finished(G_GNUC_UNUSED TilemCalcEmulator * emu,G_GNUC_UNUSED gpointer data,G_GNUC_UNUSED gboolean cancelled)247 static void tilem_macro_load_finished(G_GNUC_UNUSED TilemCalcEmulator *emu, G_GNUC_UNUSED gpointer data,
248                                G_GNUC_UNUSED gboolean cancelled)
249 {
250 	tilem_calc_emulator_begin(emu, &tilem_macro_play_main, &tilem_macro_play_finished, NULL);
251 }
252 
253 /* Load a macro from filename.
254  * If filename == NULL prompt the user
255  */
tilem_macro_load(TilemCalcEmulator * emu,char * filename)256 void tilem_macro_load(TilemCalcEmulator *emu, char* filename) {
257 
258 	/* printf("tilem_macro_load : filename : %s\n", filename); */
259 	if(!filename) {
260 		char *dir;
261 		tilem_config_get("macro", "directory/f", &dir, NULL);
262 
263 		filename = prompt_open_file("Open macro",
264 					    GTK_WINDOW(emu->ewin->window),
265 					    dir,
266 					    "Macro files", "*.txt",
267 	                                    "All files", "*",
268 					    NULL);
269 		if(dir)
270 			g_free(dir);
271 	}
272 	tilem_calc_emulator_begin(emu, &tilem_macro_load_main, &tilem_macro_load_finished, filename);
273 }
274 
275