|
2 | 2 |
|
3 | 3 | import json
|
4 | 4 | import os
|
| 5 | +import string |
5 | 6 | import warnings
|
6 | 7 | from io import BytesIO, TextIOWrapper
|
7 | 8 | from typing import Optional, Iterable, Generator, BinaryIO
|
8 | 9 | from typing_extensions import deprecated
|
9 | 10 | from zipfile import ZipFile
|
10 | 11 |
|
11 |
| -from . import base, meta, extension, monitor, sprite, asset, vlb, twconfig, comment, commons |
| 12 | +from . import base, meta, extension, monitor, sprite, asset, vlb, twconfig, comment, commons, mutation |
12 | 13 | from scratchattach.site import session
|
13 | 14 | from scratchattach.utils import exceptions
|
14 | 15 |
|
@@ -284,3 +285,96 @@ def add_monitor(self, _monitor: monitor.Monitor) -> monitor.Monitor:
|
284 | 285 | _monitor.project = self
|
285 | 286 | _monitor.reporter_id = self.new_id
|
286 | 287 | self.monitors.append(_monitor)
|
| 288 | + return _monitor |
| 289 | + |
| 290 | + def obfuscate(self, *, goto_origin: bool=True) -> None: |
| 291 | + """ |
| 292 | + Randomly set all the variable names etc. Do not upload this project to the scratch website, as it is |
| 293 | + against the community guidelines. |
| 294 | + """ |
| 295 | + # this code is an embarrassing mess. If certain features are added to sa.editor, then it could become a lot cleaner |
| 296 | + chars = string.ascii_letters + string.digits + string.punctuation |
| 297 | + |
| 298 | + def b10_to_cbase(b10: int | float): |
| 299 | + ret = '' |
| 300 | + new_base = len(chars) |
| 301 | + while b10 >= 1: |
| 302 | + ret = chars[int(b10 % new_base)] + ret |
| 303 | + b10 /= new_base |
| 304 | + |
| 305 | + return ret |
| 306 | + |
| 307 | + used = 0 |
| 308 | + |
| 309 | + def rand(): |
| 310 | + nonlocal used |
| 311 | + used += 1 |
| 312 | + |
| 313 | + return b10_to_cbase(used) |
| 314 | + |
| 315 | + for _sprite in self.sprites: |
| 316 | + procedure_mappings: dict[str, str] = {} |
| 317 | + argument_mappings: dict[str, str] = {} |
| 318 | + done_args: list[mutation.Argument] = [] |
| 319 | + |
| 320 | + for _variable in _sprite.variables: |
| 321 | + _variable.name = rand() |
| 322 | + for _list in _sprite.lists: |
| 323 | + _list.name = rand() |
| 324 | + # don't rename broadcasts as these can be dynamically called |
| 325 | + |
| 326 | + def arg_get(name: str) -> str: |
| 327 | + if name not in argument_mappings: |
| 328 | + argument_mappings[name] = rand() |
| 329 | + return argument_mappings[name] |
| 330 | + |
| 331 | + for _block in _sprite.blocks.values(): |
| 332 | + if goto_origin: |
| 333 | + _block.x, _block.y = 0, 0 |
| 334 | + |
| 335 | + if _block.opcode in ("procedures_call", "procedures_prototype", "procedures_definition"): |
| 336 | + if _block.mutation is None: |
| 337 | + continue |
| 338 | + |
| 339 | + proccode = _block.mutation.proc_code |
| 340 | + if proccode is None: |
| 341 | + continue |
| 342 | + |
| 343 | + if proccode not in procedure_mappings: |
| 344 | + parsed_ppc = _block.mutation.parsed_proc_code |
| 345 | + |
| 346 | + if parsed_ppc is None: |
| 347 | + continue |
| 348 | + |
| 349 | + new: list[str | mutation.ArgumentType] = [] |
| 350 | + for item in parsed_ppc: |
| 351 | + if isinstance(item, str): |
| 352 | + item = rand() |
| 353 | + |
| 354 | + new.append(item) |
| 355 | + |
| 356 | + new_proccode = mutation.construct_proccode(*new) |
| 357 | + procedure_mappings[proccode] = new_proccode |
| 358 | + |
| 359 | + _block.mutation.proc_code = procedure_mappings[proccode] |
| 360 | + |
| 361 | + assert _block.mutation.arguments is not None |
| 362 | + for arg in _block.mutation.arguments: |
| 363 | + if arg in done_args: |
| 364 | + continue |
| 365 | + done_args.append(arg) |
| 366 | + |
| 367 | + arg.name = arg_get(arg.name) |
| 368 | + |
| 369 | + # print(_block, _block.mutation) |
| 370 | + elif _block.opcode in ("argument_reporter_string_number", "argument_reporter_boolean"): |
| 371 | + arg_name = _block.fields["VALUE"].value |
| 372 | + assert isinstance(arg_name, str) |
| 373 | + _block.fields["VALUE"].value = arg_get(arg_name) |
| 374 | + |
| 375 | + |
| 376 | + # print(argument_mappings) |
| 377 | + |
| 378 | + if goto_origin: |
| 379 | + for _comment in _sprite.comments: |
| 380 | + _comment.x, _comment.y = 0, 0 |
0 commit comments