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.
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
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
);
36 swfedit_token_foreach (SwfeditToken
*token
, SwfeditTokenForeachFunc func
,
39 SwfeditTokenEntry
*entry
;
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
))
49 if (entry
->type
== SWFEDIT_TOKEN_OBJECT
) {
50 if (!swfedit_token_foreach (entry
->value
, func
, data
))
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 */
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 */
71 action_in_array (guint
*array
, guint action
)
84 lookup_offset (GArray
*array
, guint offset
)
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 ();
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;
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
;
122 g_assert (i
< state
->actions
->len
);
124 count
= cur
->new_actions
- 1; /* FIXME: only works as long as we append actions */
127 count
+= cur
->new_actions
;
130 g_assert (count
< 256);
131 state
->buffer
->data
[offset
+ 3 + id
] = count
;
137 modify_script_foreach (gconstpointer bytecode
, guint action
,
138 const guint8
*data
, guint len
, gpointer user_data
)
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
);
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
);
161 modify_file (SwfeditToken
*token
, guint idx
, const char *name
,
162 SwfeditTokenType type
, gconstpointer value
, gpointer data
)
165 SwfdecScript
*script
;
168 if (type
!= SWFEDIT_TOKEN_SCRIPT
)
171 state
.script
= (SwfdecScript
*) value
;
172 state
.out
= swfdec_out_open ();
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;
183 end
.new_offset
= swfdec_out_get_bits (state
.out
) / 8;
185 g_array_append_val (state
.actions
, end
);
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
);
196 /* maybe append 0 byte */
197 if (end
.offset
+ 1 == state
.script
->buffer
->length
) {
198 swfdec_out_put_u8 (state
.out
, 0);
200 g_assert (end
.offset
== state
.script
->buffer
->length
);
202 state
.buffer
= swfdec_out_close (state
.out
);
204 swfdec_script_foreach (state
.script
, fixup_jumps_foreach
, &state
);
205 g_array_free (state
.actions
, TRUE
);
207 g_print ("got a new script in %u bytes - old script was %u bytes\n",
208 state
.buffer
->length
, state
.script
->buffer
->length
);
210 script
= swfdec_script_new (state
.buffer
, state
.script
->name
, state
.script
->version
);
212 swfedit_token_set (token
, idx
, script
);
218 string_to_action_list (const char *list
)
220 char **actions
= g_strsplit (list
, ",", -1);
224 len
= g_strv_length (actions
);
225 ret
= g_new (guint
, len
+ 1);
227 for (i
= 0; i
< len
; i
++) {
228 ret
[i
] = swfdec_action_get_from_name (actions
[i
]);
230 g_printerr ("No such action \"%s\"\n", actions
[i
]);
241 main (int argc
, char **argv
)
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" },
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
);
258 g_printerr ("error parsing arguments: %s\n", error
->message
);
259 g_error_free (error
);
264 g_printerr ("Usage: %s FILENAME [OUTPUT-FILENAME]\n", argv
[0]);
268 add_trace
= string_to_action_list (add_trace_s
);
269 g_free (add_trace_s
);
270 if (add_trace
== NULL
)
273 file
= swfedit_file_new (argv
[1], &error
);
275 g_printerr ("error opening file %s: %s\n", argv
[1], error
->message
);
276 g_error_free (error
);
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
);
284 g_free (file
->filename
);
286 file
->filename
= g_strdup (argv
[2]);
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
);