sb3_decoder/structs/block/
opcodes.rs

1//! This module defines the various opcodes used in Scratch 3.0 blocks.
2//!
3//! All opcodes are divided into 9 core categories.
4//! **Note:** There are 11 more categories that aren't supported yet in this crate.
5
6use crate::error::DecodeError;
7
8/// This is the [`Opcode`] enum, which encompasses all the different opcodes used in Scratch 3.0
9/// blocks.
10///
11/// You can parse an opcode from a string slice using the [`str::parse`] method.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum Opcode {
14    /// Motion opcodes
15    Motion(MotionOpcode),
16
17    /// Looks opcodes
18    Looks(LooksOpcode),
19
20    /// Sound opcodes
21    Sound(SoundOpcode),
22
23    /// Events opcodes
24    Events(EventsOpcode),
25
26    /// Control opcodes
27    Control(ControlOpcode),
28
29    /// Sensing opcodes
30    Sensing(SensingOpcode),
31
32    /// Operators opcodes
33    Operators(OperatorsOpcode),
34
35    /// Data opcodes
36    Data(DataOpcode),
37
38    /// My Blocks (Procedures) opcodes
39    Procedures(ProceduresOpcode),
40}
41
42impl std::str::FromStr for Opcode {
43    type Err = DecodeError;
44
45    fn from_str(input: &str) -> Result<Self, Self::Err> {
46        let parts: Vec<&str> = input.splitn(2, '_').collect();
47        if parts.len() != 2 {
48            return Err(DecodeError::InvalidData(format!("Invalid opcode format: {}", input)));
49        }
50        let category = parts[0];
51        match category {
52            "motion" => Ok(Opcode::Motion(input.parse()?)),
53            "looks" => Ok(Opcode::Looks(input.parse()?)),
54            "sound" => Ok(Opcode::Sound(input.parse()?)),
55            "event" => Ok(Opcode::Events(input.parse()?)),
56            "control" => Ok(Opcode::Control(input.parse()?)),
57            "sensing" => Ok(Opcode::Sensing(input.parse()?)),
58            "operator" => Ok(Opcode::Operators(input.parse()?)),
59            "data" => Ok(Opcode::Data(input.parse()?)),
60            "procedures" => Ok(Opcode::Procedures(input.parse()?)),
61            _ => Err(DecodeError::Unknown("opcode category".to_string(), category.to_string())),
62        }
63    }
64}
65
66/// Motion opcodes
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub enum MotionOpcode {
69    MoveSteps,
70    TurnRight,
71    TurnLeft,
72    GoTo,
73    GoToXY,
74    GlideTo,
75    GlideToXY,
76    PointInDirection,
77    PointTowards,
78    ChangeXBy,
79    SetXTo,
80    ChangeYBy,
81    SetYTo,
82    IfOnEdgeBounce,
83    SetRotationStyle,
84    XPosition,
85    YPosition,
86    Direction,
87}
88
89/// Looks opcodes
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub enum LooksOpcode {
92    SayForSeconds,
93    Say,
94    ThinkForSeconds,
95    Think,
96    SwitchCostumeTo,
97    NextCostume,
98    SwitchBackdropTo,
99    SwitchBackdropToAndWait,
100    NextBackdrop,
101    ChangeSizeBy,
102    SetSizeTo,
103    ChangeEffectBy,
104    SetEffectTo,
105    ClearGraphicEffects,
106    Show,
107    Hide,
108    GoToLayer,
109    GoLayers,
110    Costume,
111    Backdrop,
112    Size,
113}
114
115/// Sound opcodes
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117pub enum SoundOpcode {
118    PlaySoundUntilDone,
119    StartSound,
120    StopAllSounds,
121    ChangeEffectBy,
122    SetEffectTo,
123    ClearSoundEffects,
124    ChangeVolumeBy,
125    SetVolumeTo,
126    Volume,
127}
128
129/// Events opcodes
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131pub enum EventsOpcode {
132    WhenFlagClicked,
133    WhenKeyPressed,
134    WhenThisSpriteClicked,
135    WhenStageClicked,
136    WhenBackdropSwitchesTo,
137    WhenGreaterThan,
138    WhenIReceive,
139    Broadcast,
140    BroadcastAndWait,
141}
142
143/// Control opcodes
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
145pub enum ControlOpcode {
146    Wait,
147    Repeat,
148    Forever,
149    If,
150    IfElse,
151    WaitUntil,
152    RepeatUntil,
153    Stop,
154    WhenIStartAsAClone,
155    CreateCloneOf,
156    DeleteThisClone,
157}
158
159/// Sensing opcodes
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161pub enum SensingOpcode {
162    Touching,
163    TouchingColor,
164    ColorTouchingColor,
165    DistanceTo,
166    AskAndWait,
167    Answer,
168    KeyPressed,
169    MouseDown,
170    MouseX,
171    MouseY,
172    SetDragMode,
173    Loudness,
174    Timer,
175    ResetTimer,
176    Of,
177    Current,
178    DaysSince2000,
179    Username,
180}
181
182/// Operators opcodes
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
184pub enum OperatorsOpcode {
185    Add,
186    Subtract,
187    Multiply,
188    Divide,
189    Random,
190    GreaterThan,
191    LessThan,
192    Equals,
193    And,
194    Or,
195    Not,
196    Join,
197    LetterOf,
198    Length,
199    Contains,
200    Mod,
201    Round,
202    MathOp,
203}
204
205/// Data opcodes
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
207pub enum DataOpcode {
208    Variable,
209    SetVariableTo,
210    ChangeVariableBy,
211    ShowVariable,
212    HideVariable,
213    List,
214    AddToList,
215    DeleteOfList,
216    DeleteAllOfList,
217    InsertInList,
218    ReplaceItemInList,
219    ItemOfList,
220    ItemNumOfList,
221    LengthOfList,
222    ListContainsItem,
223    ShowList,
224    HideList,
225}
226
227/// My Blocks (Procedures) opcodes
228#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
229pub enum ProceduresOpcode {
230    Define,
231    Call,
232    ArgumentStringNumber,
233    ArgumentBoolean,
234}
235
236macro_rules! impl_from_str {
237    ($enum:ty, $prefix:literal { $($opcode:literal => $variant:ident),* $(,)? }) => {
238        impl std::str::FromStr for $enum {
239            type Err = DecodeError;
240
241            fn from_str(input: &str) -> Result<Self, Self::Err> {
242                match input {
243                    $(
244                        concat!($prefix, "_", $opcode) => Ok(<$enum>::$variant),
245                    )*
246                    _ => Err(DecodeError::Unknown("opcode".to_string(), input.to_string()))
247                }
248            }
249        }
250    };
251}
252
253impl_from_str!(MotionOpcode, "motion" {
254    "movesteps" => MoveSteps,
255    "turnright" => TurnRight,
256    "turnleft" => TurnLeft,
257    "goto" => GoTo,
258    "gotoxy" => GoToXY,
259    "glideto" => GlideTo,
260    "glidesecstoxy" => GlideToXY,
261    "pointindirection" => PointInDirection,
262    "pointtowards" => PointTowards,
263    "changexby" => ChangeXBy,
264    "setx" => SetXTo,
265    "changeyby" => ChangeYBy,
266    "sety" => SetYTo,
267    "ifonedgebounce" => IfOnEdgeBounce,
268    "setrotationstyle" => SetRotationStyle,
269    "xposition" => XPosition,
270    "yposition" => YPosition,
271    "direction" => Direction,
272});
273
274impl_from_str!(LooksOpcode, "looks" {
275    "sayforsecs" => SayForSeconds,
276    "say" => Say,
277    "thinkforsecs" => ThinkForSeconds,
278    "think" => Think,
279    "switchcostumeto" => SwitchCostumeTo,
280    "nextcostume" => NextCostume,
281    "switchbackdropto" => SwitchBackdropTo,
282    "switchbackdroptoandwait" => SwitchBackdropToAndWait,
283    "nextbackdrop" => NextBackdrop,
284    "changesizeby" => ChangeSizeBy,
285    "setsizeto" => SetSizeTo,
286    "changeeffectby" => ChangeEffectBy,
287    "seteffectto" => SetEffectTo,
288    "cleargraphiceffects" => ClearGraphicEffects,
289    "show" => Show,
290    "hide" => Hide,
291    "gotofrontback" => GoToLayer,
292    "goforwardbackwardlayers" => GoLayers,
293    "costumenumbername" => Costume,
294    "backdropnumbername" => Backdrop,
295    "size" => Size,
296});
297
298impl_from_str!(SoundOpcode, "sound" {
299    "playuntildone" => PlaySoundUntilDone,
300    "play" => StartSound,
301    "stopallsounds" => StopAllSounds,
302    "changeeffectby" => ChangeEffectBy,
303    "seteffectto" => SetEffectTo,
304    "cleareffects" => ClearSoundEffects,
305    "changevolumeby" => ChangeVolumeBy,
306    "setvolumeto" => SetVolumeTo,
307    "volume" => Volume,
308});
309
310impl_from_str!(EventsOpcode, "event" {
311    "whenflagclicked" => WhenFlagClicked,
312    "whenkeypressed" => WhenKeyPressed,
313    "whenthisspriteclicked" => WhenThisSpriteClicked,
314    "whenstageclicked" => WhenStageClicked,
315    "whenbackdropswitchesto" => WhenBackdropSwitchesTo,
316    "whengreaterthan" => WhenGreaterThan,
317    "whenbroadcastreceived" => WhenIReceive,
318    "broadcast" => Broadcast,
319    "broadcastandwait" => BroadcastAndWait,
320});
321
322impl_from_str!(ControlOpcode, "control" {
323    "wait" => Wait,
324    "repeat" => Repeat,
325    "forever" => Forever,
326    "if" => If,
327    "if_else" => IfElse,
328    "wait_until" => WaitUntil,
329    "repeat_until" => RepeatUntil,
330    "stop" => Stop,
331    "start_as_clone" => WhenIStartAsAClone,
332    "create_clone_of" => CreateCloneOf,
333    "delete_this_clone" => DeleteThisClone,
334});
335
336impl_from_str!(SensingOpcode, "sensing" {
337    "touchingobject" => Touching,
338    "touchingcolor" => TouchingColor,
339    "coloristouchingcolor" => ColorTouchingColor,
340    "distanceto" => DistanceTo,
341    "askandwait" => AskAndWait,
342    "answer" => Answer,
343    "keypressed" => KeyPressed,
344    "mousedown" => MouseDown,
345    "mousex" => MouseX,
346    "mousey" => MouseY,
347    "setdragmode" => SetDragMode,
348    "loudness" => Loudness,
349    "timer" => Timer,
350    "resettimer" => ResetTimer,
351    "of" => Of,
352    "current" => Current,
353    "dayssince2000" => DaysSince2000,
354    "username" => Username,
355});
356
357impl_from_str!(OperatorsOpcode, "operator" {
358    "add" => Add,
359    "subtract" => Subtract,
360    "multiply" => Multiply,
361    "divide" => Divide,
362    "random" => Random,
363    "gt" => GreaterThan,
364    "lt" => LessThan,
365    "equals" => Equals,
366    "and" => And,
367    "or" => Or,
368    "not" => Not,
369    "join" => Join,
370    "letter_of" => LetterOf,
371    "length" => Length,
372    "contains" => Contains,
373    "mod" => Mod,
374    "round" => Round,
375    "mathop" => MathOp,
376});
377
378impl_from_str!(DataOpcode, "data" {
379    "variable" => Variable,
380    "setvariableto" => SetVariableTo,
381    "changevariableby" => ChangeVariableBy,
382    "showvariable" => ShowVariable,
383    "hidevariable" => HideVariable,
384    "listcontent" => List,
385    "addtolist" => AddToList,
386    "deleteoflist" => DeleteOfList,
387    "deletealloflist" => DeleteAllOfList,
388    "insertatlist" => InsertInList,
389    "replaceitemoflist" => ReplaceItemInList,
390    "itemoflist" => ItemOfList,
391    "itemnumoflist" => ItemNumOfList,
392    "lengthoflist" => LengthOfList,
393    "listcontainsitem" => ListContainsItem,
394    "showlist" => ShowList,
395    "hidelist" => HideList,
396});
397
398impl_from_str!(ProceduresOpcode, "procedures" {
399    "definition" => Define,
400    "call" => Call,
401    "argument_reporter_string_number" => ArgumentStringNumber,
402    "argument_reporter_boolean" => ArgumentBoolean,
403});