add a gradient test
[swfdec.git] / test / swfscript.c
blob4e2a9e4e253c5e3d4c5f2438224c2e98088c9a73
1 /* Swfedit
2 * Copyright (C) 2007 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <gtk/gtk.h>
25 #include "libswfdec/swfdec_script_internal.h"
26 #include "swfdec_out.h"
27 #include "swfedit_file.h"
29 /* the stuff we look for */
30 guint *add_trace = NULL;
32 typedef gboolean ( *SwfeditTokenForeachFunc) (SwfeditToken *token, guint idx,
33 const char *name, SwfeditTokenType type, gconstpointer value, gpointer data);
35 static gboolean
36 swfedit_token_foreach (SwfeditToken *token, SwfeditTokenForeachFunc func,
37 gpointer data)
39 SwfeditTokenEntry *entry;
40 guint i;
42 g_return_val_if_fail (SWFEDIT_IS_TOKEN (token), FALSE);
43 g_return_val_if_fail (func != NULL, FALSE);
45 for (i = 0; i < token->tokens->len; i++) {
46 entry = &g_array_index (token->tokens, SwfeditTokenEntry, i);
47 if (!func (token, i, entry->name, entry->type, entry->value, data))
48 return FALSE;
49 if (entry->type == SWFEDIT_TOKEN_OBJECT) {
50 if (!swfedit_token_foreach (entry->value, func, data))
51 return FALSE;
54 return TRUE;
57 typedef struct {
58 guint offset; /* offset in bytes from start of script */
59 guint new_offset; /* new offset in bytes from start of script */
60 guint new_actions; /* number of actions in new script */
61 } Action;
63 typedef struct {
64 SwfdecScript * script; /* the original script */
65 SwfdecOut * out; /* output for new script or NULL when buffer is set */
66 SwfdecBuffer * buffer; /* buffer containing new script or NULL while constructing */
67 GArray * actions; /* all actions in the script */
68 } State;
70 static gboolean
71 action_in_array (guint *array, guint action)
73 if (array == NULL)
74 return FALSE;
75 while (*array != 0) {
76 if (*array == action)
77 return TRUE;
78 array++;
80 return FALSE;
83 static guint
84 lookup_offset (GArray *array, guint offset)
86 guint i;
87 for (i = 0; i < array->len; i++) {
88 Action *action = &g_array_index (array, Action, i);
89 if (action->offset == offset)
90 return action->new_offset;
92 g_assert_not_reached ();
93 return 0;
96 static gboolean
97 fixup_jumps_foreach (gconstpointer bytecode, guint action,
98 const guint8 *data, guint len, gpointer user_data)
100 State *state = user_data;
102 if (action == 0x99 || action == 0x9d) {
103 guint offset = (guint8 *) bytecode - state->script->buffer->data;
104 guint jump_offset = offset + 5 + GINT16_FROM_LE (*((gint16 *) data));
105 offset = lookup_offset (state->actions, offset);
106 jump_offset = lookup_offset (state->actions, jump_offset);
107 *((gint16 *) &state->buffer->data[offset + 3]) =
108 GINT16_TO_LE (jump_offset - offset - 5);
110 if (action == 0x8a || action == 0x8d) {
111 Action *cur = NULL; /* silence gcc */
112 guint id = action == 0x8a ? 2 : 0;
113 guint i, count;
114 guint offset = (guint8 *) bytecode - state->script->buffer->data;
115 for (i = 0; i < state->actions->len; i++) {
116 cur = &g_array_index (state->actions, Action, i);
117 if (cur->offset == offset) {
118 offset = cur->new_offset;
119 break;
122 g_assert (i < state->actions->len);
123 i = data[id];
124 count = cur->new_actions - 1; /* FIXME: only works as long as we append actions */
125 while (i > 0) {
126 cur++;
127 count += cur->new_actions;
128 i--;
130 g_assert (count < 256);
131 state->buffer->data[offset + 3 + id] = count;
133 return TRUE;
136 static gboolean
137 modify_script_foreach (gconstpointer bytecode, guint action,
138 const guint8 *data, guint len, gpointer user_data)
140 Action next;
141 State *state = user_data;
143 next.offset = (guint8 *) bytecode - state->script->buffer->data;
144 next.new_offset = swfdec_out_get_bits (state->out) / 8;
145 next.new_actions = 1;
146 swfdec_out_put_u8 (state->out, action);
147 if (action & 0x80) {
148 swfdec_out_put_u16 (state->out, len);
149 swfdec_out_put_data (state->out, data, len);
151 if (action_in_array (add_trace, action)) {
152 swfdec_out_put_u8 (state->out, 0x4c); /* PushDuplicate */
153 swfdec_out_put_u8 (state->out, 0x26); /* Trace */
154 next.new_actions += 2;
156 g_array_append_val (state->actions, next);
157 return TRUE;
160 static gboolean
161 modify_file (SwfeditToken *token, guint idx, const char *name,
162 SwfeditTokenType type, gconstpointer value, gpointer data)
164 Action end;
165 SwfdecScript *script;
166 State state;
168 if (type != SWFEDIT_TOKEN_SCRIPT)
169 return TRUE;
171 state.script = (SwfdecScript *) value;
172 state.out = swfdec_out_open ();
173 state.buffer = NULL;
174 state.actions = g_array_new (FALSE, FALSE, sizeof (Action));
175 swfdec_script_foreach (state.script, modify_script_foreach, &state);
176 /* compute end offset */
177 end.offset = g_array_index (state.actions, Action, state.actions->len - 1).offset;
178 if (state.script->buffer->data[end.offset] & 0x80) {
179 end.offset += GUINT16_FROM_LE (*((guint16* ) &state.script->buffer->data[end.offset + 1])) + 3;
180 } else {
181 end.offset++;
183 end.new_offset = swfdec_out_get_bits (state.out) / 8;
184 end.new_actions = 0;
185 g_array_append_val (state.actions, end);
186 #if 0
188 guint i;
189 for (i = 0; i < state.actions->len; i++) {
190 Action *action = &g_array_index (state.actions, Action, i);
191 g_print ("%u %u => %u (%u actions)\n", i, action->offset,
192 action->new_offset, action->new_actions);
195 #endif
196 /* maybe append 0 byte */
197 if (end.offset + 1 == state.script->buffer->length) {
198 swfdec_out_put_u8 (state.out, 0);
199 } else {
200 g_assert (end.offset == state.script->buffer->length);
202 state.buffer = swfdec_out_close (state.out);
203 state.out = NULL;
204 swfdec_script_foreach (state.script, fixup_jumps_foreach, &state);
205 g_array_free (state.actions, TRUE);
206 #if 0
207 g_print ("got a new script in %u bytes - old script was %u bytes\n",
208 state.buffer->length, state.script->buffer->length);
209 #endif
210 script = swfdec_script_new (state.buffer, state.script->name, state.script->version);
211 g_assert (script);
212 swfedit_token_set (token, idx, script);
214 return TRUE;
217 static guint *
218 string_to_action_list (const char *list)
220 char **actions = g_strsplit (list, ",", -1);
221 guint *ret;
222 guint i, len;
224 len = g_strv_length (actions);
225 ret = g_new (guint, len + 1);
226 ret[len] = 0;
227 for (i = 0; i < len; i++) {
228 ret[i] = swfdec_action_get_from_name (actions[i]);
229 if (ret[i] == 0) {
230 g_printerr ("No such action \"%s\"\n", actions[i]);
231 g_free (actions);
232 g_free (ret);
233 return NULL;
236 g_free (actions);
237 return ret;
241 main (int argc, char **argv)
243 SwfeditFile *file;
244 GError *error = NULL;
245 char *add_trace_s = NULL;
246 GOptionEntry options[] = {
247 { "add-trace", 't', 0, G_OPTION_ARG_STRING, &add_trace_s, "list of actions to trace", "ACTION, ACTION" },
248 { NULL }
250 GOptionContext *ctx;
252 ctx = g_option_context_new ("");
253 g_option_context_add_main_entries (ctx, options, "options");
254 g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
255 g_option_context_parse (ctx, &argc, &argv, &error);
256 g_option_context_free (ctx);
257 if (error) {
258 g_printerr ("error parsing arguments: %s\n", error->message);
259 g_error_free (error);
260 return 1;
263 if (argc < 2) {
264 g_printerr ("Usage: %s FILENAME [OUTPUT-FILENAME]\n", argv[0]);
265 return 1;
267 if (add_trace_s) {
268 add_trace = string_to_action_list (add_trace_s);
269 g_free (add_trace_s);
270 if (add_trace == NULL)
271 return 1;
273 file = swfedit_file_new (argv[1], &error);
274 if (file == NULL) {
275 g_printerr ("error opening file %s: %s\n", argv[1], error->message);
276 g_error_free (error);
277 return 1;
279 if (!swfedit_token_foreach (SWFEDIT_TOKEN (file), modify_file, NULL)) {
280 g_printerr ("modifying file %s failed.\n", argv[1]);
281 g_object_unref (file);
282 return 1;
284 g_free (file->filename);
285 if (argc > 2) {
286 file->filename = g_strdup (argv[2]);
287 } else {
288 file->filename = g_strdup_printf ("%s.out.swf", argv[1]);
290 if (!swfedit_file_save (file, &error)) {
291 g_printerr ("Error saving file: %s\n", error->message);
292 g_error_free (error);
294 g_print ("saved modified file to %s\n", file->filename);
295 g_object_unref (file);
296 return 0;