File size: 15,649 Bytes
a522962
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
import json

def update_scratch_project_data(project_data):
    """

    Updates variable and broadcast definitions in a Scratch project JSON,

    populating the 'variables' and 'broadcasts' sections of the Stage target

    and extracting initial values for variables.



    Args:

        project_data (dict): The loaded JSON data of the Scratch project.



    Returns:

        dict: The updated project JSON data.

    """

    stage_target = None
    for target in project_data['targets']:
        if target.get('isStage'):
            stage_target = target
            break

    if stage_target is None:
        print("Error: Stage target not found in the project data.")
        return project_data

    # Ensure 'variables' and 'broadcasts' exist in the Stage target
    if "variables" not in stage_target:
        stage_target["variables"] = {}
    if "broadcasts" not in stage_target:
        stage_target["broadcasts"] = {}

    # Helper function to recursively find and update variable/broadcast fields
    def process_dict(obj):
        if isinstance(obj, dict):
            # Check for "data_setvariableto" opcode to extract initial values
            if obj.get("opcode") == "data_setvariableto":
                variable_field = obj.get("fields", {}).get("VARIABLE")
                value_input = obj.get("inputs", {}).get("VALUE")

                if variable_field and isinstance(variable_field, list) and len(variable_field) == 2:
                    var_name = variable_field[0]
                    var_id = variable_field[1]

                    initial_value = ""
                    if value_input and isinstance(value_input, list) and len(value_input) > 1 and \
                       isinstance(value_input[1], list) and len(value_input[1]) > 1:
                        # Extract value from various formats, e.g., [1, [10, "0"]] or [3, [12, "score", "id"], [10, "0"]]
                        if value_input[1][0] == 10: # Direct value like [10, "0"]
                            initial_value = str(value_input[1][1])
                        elif value_input[1][0] == 12 and len(value_input) > 2 and isinstance(value_input[2], list) and value_input[2][0] == 10: # Variable reference with initial value block
                             initial_value = str(value_input[2][1])
                        elif isinstance(value_input[1], (str, int, float)): # For direct number/string inputs
                            initial_value = str(value_input[1])


                    # Add/update the variable in the Stage's 'variables' with its initial value
                    stage_target["variables"][var_id] = [var_name, initial_value]


            for key, value in obj.items():
                # Process variable definitions in 'fields' (for blocks that define variables like 'show variable')
                if key == "VARIABLE" and isinstance(value, list) and len(value) == 2:
                    var_name = value[0]
                    var_id = value[1]
                    # Only add if not already defined with an initial value from set_variableto
                    if var_id not in stage_target["variables"]:
                        stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet
                    elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different
                        stage_target["variables"][var_id][0] = var_name


                # Process broadcast definitions in 'inputs' (BROADCAST_INPUT)
                elif key == "BROADCAST_INPUT" and isinstance(value, list) and len(value) == 2 and \
                     isinstance(value[1], list) and len(value[1]) == 3 and value[1][0] == 11:
                    broadcast_name = value[1][1]
                    broadcast_id = value[1][2]
                    # Add/update the broadcast in the Stage's 'broadcasts'
                    stage_target["broadcasts"][broadcast_id] = broadcast_name

                # Process broadcast definitions in 'fields' (BROADCAST_OPTION)
                elif key == "BROADCAST_OPTION" and isinstance(value, list) and len(value) == 2:
                    broadcast_name = value[0]
                    broadcast_id = value[1]
                    # Add/update the broadcast in the Stage's 'broadcasts'
                    stage_target["broadcasts"][broadcast_id] = broadcast_name

                # Recursively call for nested dictionaries or lists
                process_dict(value)
        elif isinstance(obj, list):
            for i, item in enumerate(obj):
                # Process variable references in 'inputs' (like [12, "score", "id"])
                if isinstance(item, list) and len(item) == 3 and item[0] == 12:
                    var_name = item[1]
                    var_id = item[2]
                    # Only add if not already defined with an initial value from set_variableto
                    if var_id not in stage_target["variables"]:
                        stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet
                    elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different
                        stage_target["variables"][var_id][0] = var_name

                process_dict(item)

    # Iterate through all targets to process their blocks
    for target in project_data['targets']:
        if "blocks" in target:
            for block_id, block_data in target["blocks"].items():
                process_dict(block_data)

    return project_data

def deduplicate_variables(project_data):
    """

    Removes duplicate variable entries in the 'variables' dictionary of the Stage target,

    prioritizing entries with non-empty values.



    Args:

        project_data (dict): The loaded JSON data of the Scratch project.



    Returns:

        dict: The updated project JSON data with deduplicated variables.

    """

    stage_target = None
    for target in project_data['targets']:
        if target.get('isStage'):
            stage_target = target
            break

    if stage_target is None:
        print("Error: Stage target not found in the project data.")
        return project_data

    if "variables" not in stage_target:
        return project_data # No variables to deduplicate

    # Use a temporary dictionary to store the preferred variable entry by name
    # Format: {variable_name: [variable_id, variable_name, variable_value]}
    resolved_variables = {}

    for var_id, var_info in stage_target["variables"].items():
        var_name = var_info[0]
        var_value = var_info[1]

        if var_name not in resolved_variables:
            # If the variable name is not yet seen, add it
            resolved_variables[var_name] = [var_id, var_name, var_value]
        else:
            # If the variable name is already seen, decide which one to keep
            existing_id, existing_name, existing_value = resolved_variables[var_name]

            # Prioritize the entry with a non-empty value
            if var_value != "" and existing_value == "":
                resolved_variables[var_name] = [var_id, var_name, var_value]
            # If both have non-empty values, or both are empty, keep the current one (arbitrary choice, but consistent)
            # The current logic will effectively keep the last one encountered that has a value,
            # or the very last one if all are empty.
            elif var_value != "" and existing_value != "":
                 # If there are multiple non-empty values for the same variable name
                 # this keeps the one from the most recent iteration.
                 # For the given example, this will correctly keep "5".
                resolved_variables[var_name] = [var_id, var_name, var_value]
            elif var_value == "" and existing_value == "":
                # If both are empty, just keep the current one (arbitrary)
                resolved_variables[var_name] = [var_id, var_name, var_value]


    # Reconstruct the 'variables' dictionary using the resolved entries
    new_variables_dict = {}
    for var_name, var_data in resolved_variables.items():
        var_id_to_keep = var_data[0]
        var_name_to_keep = var_data[1]
        var_value_to_keep = var_data[2]
        new_variables_dict[var_id_to_keep] = [var_name_to_keep, var_value_to_keep]

    stage_target["variables"] = new_variables_dict

    return project_data

# Example usage with your provided JSON:
if __name__ == "__main__":
    json_data = {
        "targets": [
            {"isStage": True, "name": "Stage", "variables": {},"lists": {}, "broadcasts": {}, "blocks": {
        "event_whenflagclicked_1": {
            "opcode": "event_whenflagclicked",
            "inputs": {},
            "fields": {},
            "shadow": False,
            "topLevel": True,
            "parent": None,
            "next": "data_setvariableto_1"
        },
        "data_setvariableto_1": {
            "opcode": "data_setvariableto",
            "inputs": {
                "VALUE": [
                    1,
                    [
                        4,
                        "0"
                    ]
                ]
            },
            "fields": {
                "VARIABLE": [
                    "score",
                    "$ELgBAKpb[&l+DX$EMK4"
                ]
            },
            "shadow": False,
            "topLevel": False,
            "parent": "event_whenflagclicked_1",
            "next": "data_setvariableto_2"
        },
        "data_setvariableto_2": {
            "opcode": "data_setvariableto",
            "inputs": {
                "VALUE": [
                    1,
                    [
                        4,
                        "3"
                    ]
                ]
            },
            "fields": {
                "VARIABLE": [
                    "lives",
                    "Gb]1jA+H{h_6z1^Fn!-a"
                ]
            },
            "shadow": False,
            "topLevel": False,
            "parent": "data_setvariableto_1",
            "next": "data_showvariable_1"
        },
        "data_showvariable_1": {
            "opcode": "data_showvariable",
            "inputs": {},
            "fields": {
                "VARIABLE": [
                    "score",
                    "$ELgBAKpb[&l+DX$EMK4"
                ]
            },
            "shadow": False,
            "topLevel": False,
            "parent": "data_setvariableto_2",
            "next": "data_showvariable_2"
        },
        "data_showvariable_2": {
            "opcode": "data_showvariable",
            "inputs": {},
            "fields": {
                "VARIABLE": [
                    "lives",
                    "Gb]1jA+H{h_6z1^Fn!-a"
                ]
            },
            "shadow": False,
            "topLevel": False,
            "parent": "data_showvariable_1",
            "next": "event_broadcast_1"
        },
        "event_broadcast_1": {
            "opcode": "event_broadcast",
            "inputs": {
                "BROADCAST_INPUT": [
                    1,
                    [
                        11,
                        "Game Start",
                        "hN1d@^S}e)H~8(qp)rGN"
                    ]
                ]
            },
            "fields": {},
            "shadow": False,
            "topLevel": False,
            "parent": "data_showvariable_2",
            "next": None
        }
    }, "comments": {}, "currentCostume": 1, "costumes": [{"name": "backdrop1", "dataFormat": "svg", "assetId": "cd21514d0531fdffb22204e0ec5ed84a", "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", "rotationCenterX": 240, "rotationCenterY": 180}, {"name": "Blue Sky", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "e7c147730f19d284bcd7b3f00af19bb6", "rotationCenterX": 240, "rotationCenterY": 180}], "sounds": [{"name": "pop", "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", "dataFormat": "wav", "format": "", "rate": 48000, "sampleCount": 1123, "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav"}], "volume": 100, "layerOrder": 0, "tempo": 60, "videoTransparency": 50, "videoState": "on", "textToSpeechLanguage": None},
            {"isStage": False, "name": "Sprite1", "variables": {}, "lists": {}, "broadcasts": {}, "blocks": {}, "comments": {}, "currentCostume": 0, "costumes": [{"name": "costume1", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "bcf454acf82e4504149f7ffe07081dbc", "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", "rotationCenterX": 48, "rotationCenterY": 50}, {"name": "costume2", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "0fb9be3e8397c983338cb71dc84d0b25", "md5ext": "0fb9be3e8397c983338cb71dc84d0b25.svg", "rotationCenterX": 46, "rotationCenterY": 53}], "sounds": [{"name": "Meow", "assetId": "83c36d806dc92327b9e7049a565c6bff", "dataFormat": "wav", "format": "", "rate": 48000, "sampleCount": 40681, "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav"}], "volume": 100, "layerOrder": 1, "visible": True, "x": 0, "y": -120, "size": 100, "direction": 90, "draggable": False, "rotationStyle": "all around"},
            {"isStage": False, "name": "Soccer Ball", "variables": {}, "lists": {}, "broadcasts": {}, "blocks": {}, "comments": {}, "currentCostume": 0, "costumes": [{"name": "soccer ball", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", "rotationCenterX": 23, "rotationCenterY": 22}], "sounds": [{"name": "basketball bounce", "assetId": "1727f65b5f22d151685b8e5917456a60", "dataFormat": "wav", "format": "adpcm", "rate": 22050, "sampleCount": 8129, "md5ext": "1727f65b5f22d151685b8e5917456a60.wav"}], "volume": 100, "layerOrder": 2, "visible": True, "x": -130, "y": -60, "size": 100, "direction": 90, "draggable": False, "rotationStyle": "all around"}], "monitors": [{"id": "76*2udMupx@8!=)9Uqvj", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "score"}, "spriteName": None, "value": "0", "width": 0, "height": 0, "x": 5, "y": 5, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "YiV;2%+lgjnJ$|*Jy.{H", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "cloud Var"}, "spriteName": None, "value": 0, "width": 0, "height": 0, "x": 5, "y": 32, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "@D/}fdt{0XaJ`kRE0t~F", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "lives"}, "spriteName": None, "value": "3", "width": 0, "height": 0, "x": 5, "y": 59, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "^R,uz7yRjtK`=uP~6SN4", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "speed"}, "spriteName": None, "value": "5", "width": 0, "height": 0, "x": 5, "y": 86, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}], "extensions": [], "meta": {"semver": "3.0.0", "vm": "11.3.0", "agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"}}


    updated_json_data = update_scratch_project_data(json_data)
    updated_json_data = deduplicate_variables(json_data)
    print(json.dumps(updated_json_data, indent=1))