avm1_tree/
lib.rs

1use serde::{Deserialize, Serialize};
2
3pub use self::value::Value;
4
5pub mod actions;
6pub mod cfg_actions;
7pub mod cfg_blocks;
8mod float_is;
9mod helpers;
10mod value;
11
12#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
13#[serde(tag = "action", rename_all = "kebab-case")]
14pub enum Action {
15  Unknown(actions::UnknownAction),
16  Add,
17  And,
18  CastOp,
19  CloneSprite,
20  Divide,
21  Equals,
22  EndDrag,
23  FsCommand2,
24  GetProperty,
25  GetVariable,
26  ImplementsOp,
27  Less,
28  MbStringLength,
29  Multiply,
30  NextFrame,
31  Not,
32  Or,
33  PrevFrame,
34  Play,
35  Pop,
36  RandomNumber,
37  RemoveSprite,
38  SetProperty,
39  SetTarget2,
40  SetVariable,
41  StartDrag,
42  Stop,
43  StopSounds,
44  StringAdd,
45  StringEquals,
46  StringExtract,
47  StringLength,
48  StringLess,
49  Subtract,
50  Throw,
51  ToInteger,
52  ToggleQuality,
53  Trace,
54  // 0x31
55  CharToAscii,
56  // 0x32
57  AsciiToChar,
58  // 0x33
59  GetTime,
60  // 0x34
61  MbStringExtract,
62  // 0x35
63  MbCharToAscii,
64  // 0x36
65  MbAsciiToChar,
66  // 0x37
67  Delete,
68  // 0x3a
69  Delete2,
70  // 0x3b
71  DefineLocal,
72  // 0x3c
73  CallFunction,
74  // 0x3d
75  Return,
76  // 0x3e
77  Modulo,
78  // 0x3f
79  NewObject,
80  // 0x40
81  DefineLocal2,
82  // 0x41
83  InitArray,
84  // 0x42
85  InitObject,
86  // 0x43
87  TypeOf,
88  // 0x44
89  TargetPath,
90  // 0x45
91  Enumerate,
92  // 0x46
93  Add2,
94  // 0x47
95  Less2,
96  // 0x48
97  Equals2,
98  // 0x49
99  ToNumber,
100  // 0x4a
101  ToString,
102  // 0x4b
103  PushDuplicate,
104  // 0x4c
105  StackSwap,
106  // 0x4d
107  GetMember,
108  // 0x4e
109  SetMember,
110  // 0x4f
111  Increment,
112  // 0x50
113  Decrement,
114  // 0x51
115  CallMethod,
116  // 0x52
117  NewMethod,
118  // 0x53
119  InstanceOf,
120  // 0x54
121  Enumerate2,
122  // 0x55
123  BitAnd,
124  // 0x60
125  BitOr,
126  // 0x61
127  BitXor,
128  // 0x62
129  BitLShift,
130  // 0x63
131  BitRShift,
132  // 0x64
133  BitURShift,
134  // 0x65
135  StrictEquals,
136  // 0x66
137  Greater,
138  // 0x67
139  StringGreater,
140  // 0x68
141  Extends,
142  // 0x69
143  GotoFrame(actions::GotoFrame),
144  // 0x81
145  GetUrl(actions::GetUrl),
146  // 0x83
147  StoreRegister(actions::StoreRegister),
148  // 0x87
149  ConstantPool(actions::ConstantPool),
150  // 0x88
151  WaitForFrame(actions::WaitForFrame),
152  // 0x8a
153  SetTarget(actions::SetTarget),
154  // 0x8b
155  GotoLabel(actions::GoToLabel),
156  // 0x8c
157  WaitForFrame2(actions::WaitForFrame2),
158  // 0x8d
159  DefineFunction2(actions::DefineFunction2),
160  // 0x8e
161  Try(actions::Try),
162  // 0x8f
163  With(actions::With),
164  // 0x94
165  Push(actions::Push),
166  // 0x96
167  Jump(actions::Jump),
168  // 0x99
169  GetUrl2(actions::GetUrl2),
170  // 0x9a
171  DefineFunction(actions::DefineFunction),
172  // 0x9b
173  If(actions::If),
174  // 0x9d
175  Call,
176  // 0x9e
177  GotoFrame2(actions::GotoFrame2),
178  // 0x9f
179}
180
181#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
182#[serde(rename_all = "snake_case")]
183pub struct Cfg {
184  pub blocks: Vec<CfgBlock>,
185}
186
187#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
188#[serde(rename_all = "snake_case")]
189pub struct CfgLabel(pub String);
190
191#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
192#[serde(tag = "type", rename_all = "kebab-case")]
193pub enum CfgBlock {
194  Error(cfg_blocks::CfgErrorBlock),
195  If(cfg_blocks::CfgIfBlock),
196  Return(cfg_blocks::CfgReturnBlock),
197  Simple(cfg_blocks::CfgSimpleBlock),
198  Throw(cfg_blocks::CfgThrowBlock),
199  Try(cfg_blocks::CfgTryBlock),
200  WaitForFrame(cfg_blocks::CfgWaitForFrameBlock),
201  WaitForFrame2(cfg_blocks::CfgWaitForFrame2Block),
202  With(cfg_blocks::CfgWithBlock),
203}
204
205/// Similar to `Action` but no `Jump`, `If` `Throw`, `Return`, `Try`,
206/// `WaitForFrame`, `WaitForFrame2` and `With`, also different `DefineFunction`
207/// and `DefineFunction2`.
208#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
209#[serde(tag = "action", rename_all = "kebab-case")]
210pub enum CfgAction {
211  Unknown(actions::UnknownAction),
212  Add,
213  And,
214  CastOp,
215  CloneSprite,
216  Divide,
217  Equals,
218  EndDrag,
219  FsCommand2,
220  GetProperty,
221  GetVariable,
222  ImplementsOp,
223  Less,
224  MbStringLength,
225  Multiply,
226  NextFrame,
227  Not,
228  Or,
229  PrevFrame,
230  Play,
231  Pop,
232  RandomNumber,
233  RemoveSprite,
234  SetProperty,
235  SetTarget2,
236  SetVariable,
237  StartDrag,
238  Stop,
239  StopSounds,
240  StringAdd,
241  StringEquals,
242  StringExtract,
243  StringLength,
244  StringLess,
245  Subtract,
246  ToInteger,
247  ToggleQuality,
248  Trace,
249  CharToAscii,
250  AsciiToChar,
251  GetTime,
252  MbStringExtract,
253  MbCharToAscii,
254  MbAsciiToChar,
255  Delete,
256  Delete2,
257  DefineLocal,
258  CallFunction,
259  Modulo,
260  NewObject,
261  DefineLocal2,
262  InitArray,
263  InitObject,
264  TypeOf,
265  TargetPath,
266  Enumerate,
267  Add2,
268  Less2,
269  Equals2,
270  ToNumber,
271  ToString,
272  PushDuplicate,
273  StackSwap,
274  GetMember,
275  SetMember,
276  Increment,
277  Decrement,
278  CallMethod,
279  NewMethod,
280  InstanceOf,
281  Enumerate2,
282  BitAnd,
283  BitOr,
284  BitXor,
285  BitLShift,
286  BitRShift,
287  BitURShift,
288  StrictEquals,
289  Greater,
290  StringGreater,
291  Extends,
292  GotoFrame(actions::GotoFrame),
293  GetUrl(actions::GetUrl),
294  StoreRegister(actions::StoreRegister),
295  ConstantPool(actions::ConstantPool),
296  SetTarget(actions::SetTarget),
297  GotoLabel(actions::GoToLabel),
298  StrictMode(actions::StrictMode),
299  DefineFunction2(cfg_actions::CfgDefineFunction2),
300  Push(actions::Push),
301  GetUrl2(actions::GetUrl2),
302  DefineFunction(cfg_actions::CfgDefineFunction),
303  Call,
304  GotoFrame2(actions::GotoFrame2),
305}
306
307#[cfg(test)]
308mod tests {
309  use crate::Cfg;
310  use ::test_generator::test_resources;
311  use std::path::Path;
312
313  #[test_resources("../tests/avm1/[!.]*/*/")]
314  fn test_cfg(path: &str) {
315    let path: &Path = Path::new(path);
316    let name = path
317      .components()
318      .last()
319      .unwrap()
320      .as_os_str()
321      .to_str()
322      .expect("Failed to retrieve sample name");
323
324    if name == "corrupted-push" || name == "try-jump-to-catch-throw-finally" {
325      return;
326    }
327
328    let cfg_path = path.join("cfg.json");
329
330    let value_json = ::std::fs::read_to_string(cfg_path).expect("Failed to read CFG file");
331
332    let value: Cfg = serde_json_v8::from_str(&value_json).expect("Failed to read CFG");
333
334    let output_json = serde_json_v8::to_string_pretty(&value).expect("Failed to write CFG");
335    let output_json = format!("{}\n", output_json);
336
337    assert_eq!(output_json, value_json)
338  }
339}
340
341#[cfg(test)]
342mod e2e_raw_tests {
343  use super::*;
344  use ::test_generator::test_resources;
345
346  #[test_resources("../tests/raw/*.json")]
347  fn test_parse_action(path: &str) {
348    let file = ::std::fs::File::open(path).unwrap();
349    let reader = ::std::io::BufReader::new(file);
350    // Check that we can parse the test case without issues
351    serde_json::from_reader::<_, Action>(reader).unwrap();
352  }
353}