polyfile-weave 0.5.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of polyfile-weave might be problematic. Click here for more details.
- polyfile/__init__.py +15 -0
- polyfile/__main__.py +394 -0
- polyfile/arithmetic.py +27 -0
- polyfile/ast.py +114 -0
- polyfile/debugger.py +1039 -0
- polyfile/expressions.py +346 -0
- polyfile/fileutils.py +343 -0
- polyfile/html.py +135 -0
- polyfile/http/__init__.py +1 -0
- polyfile/http/defacto.py +37 -0
- polyfile/http/deprecated.py +51 -0
- polyfile/http/experimental.py +67 -0
- polyfile/http/http_11.py +548 -0
- polyfile/http/matcher.py +37 -0
- polyfile/http/structured_headers.py +48 -0
- polyfile/iterators.py +72 -0
- polyfile/jpeg.py +24 -0
- polyfile/kaitai/__init__.py +0 -0
- polyfile/kaitai/compiler.py +156 -0
- polyfile/kaitai/parser.py +312 -0
- polyfile/kaitai/parsers/__init__.py +0 -0
- polyfile/kaitai/parsers/aix_utmp.py +116 -0
- polyfile/kaitai/parsers/allegro_dat.py +367 -0
- polyfile/kaitai/parsers/andes_firmware.py +64 -0
- polyfile/kaitai/parsers/android_bootldr_asus.py +105 -0
- polyfile/kaitai/parsers/android_bootldr_huawei.py +181 -0
- polyfile/kaitai/parsers/android_bootldr_qcom.py +217 -0
- polyfile/kaitai/parsers/android_dto.py +138 -0
- polyfile/kaitai/parsers/android_img.py +319 -0
- polyfile/kaitai/parsers/android_nanoapp_header.py +83 -0
- polyfile/kaitai/parsers/android_opengl_shaders_cache.py +151 -0
- polyfile/kaitai/parsers/android_sparse.py +237 -0
- polyfile/kaitai/parsers/android_super.py +401 -0
- polyfile/kaitai/parsers/apm_partition_table.py +196 -0
- polyfile/kaitai/parsers/apple_single_double.py +180 -0
- polyfile/kaitai/parsers/asn1_der.py +235 -0
- polyfile/kaitai/parsers/au.py +138 -0
- polyfile/kaitai/parsers/avantes_roh60.py +112 -0
- polyfile/kaitai/parsers/avi.py +296 -0
- polyfile/kaitai/parsers/bcd.py +111 -0
- polyfile/kaitai/parsers/bitcoin_transaction.py +210 -0
- polyfile/kaitai/parsers/blender_blend.py +334 -0
- polyfile/kaitai/parsers/bmp.py +780 -0
- polyfile/kaitai/parsers/bson.py +411 -0
- polyfile/kaitai/parsers/btrfs_stream.py +318 -0
- polyfile/kaitai/parsers/bytes_with_io.py +27 -0
- polyfile/kaitai/parsers/chrome_pak.py +194 -0
- polyfile/kaitai/parsers/code_6502.py +456 -0
- polyfile/kaitai/parsers/compressed_resource.py +217 -0
- polyfile/kaitai/parsers/cpio_old_le.py +154 -0
- polyfile/kaitai/parsers/cramfs.py +344 -0
- polyfile/kaitai/parsers/creative_voice_file.py +342 -0
- polyfile/kaitai/parsers/dbf.py +274 -0
- polyfile/kaitai/parsers/dcmp_0.py +664 -0
- polyfile/kaitai/parsers/dcmp_1.py +422 -0
- polyfile/kaitai/parsers/dcmp_2.py +312 -0
- polyfile/kaitai/parsers/dcmp_variable_length_integer.py +66 -0
- polyfile/kaitai/parsers/dex.py +1086 -0
- polyfile/kaitai/parsers/dicom.py +4370 -0
- polyfile/kaitai/parsers/dime_message.py +201 -0
- polyfile/kaitai/parsers/dns_packet.py +569 -0
- polyfile/kaitai/parsers/doom_wad.py +654 -0
- polyfile/kaitai/parsers/dos_datetime.py +191 -0
- polyfile/kaitai/parsers/dos_mz.py +172 -0
- polyfile/kaitai/parsers/ds_store.py +513 -0
- polyfile/kaitai/parsers/dtb.py +310 -0
- polyfile/kaitai/parsers/dune_2_pak.py +126 -0
- polyfile/kaitai/parsers/edid.py +472 -0
- polyfile/kaitai/parsers/efivar_signature_list.py +331 -0
- polyfile/kaitai/parsers/elf.py +2482 -0
- polyfile/kaitai/parsers/ethernet_frame.py +114 -0
- polyfile/kaitai/parsers/exif.py +723 -0
- polyfile/kaitai/parsers/ext2.py +537 -0
- polyfile/kaitai/parsers/fallout2_dat.py +187 -0
- polyfile/kaitai/parsers/fallout_dat.py +156 -0
- polyfile/kaitai/parsers/fasttracker_xm_module.py +558 -0
- polyfile/kaitai/parsers/ftl_dat.py +90 -0
- polyfile/kaitai/parsers/genmidi_op2.py +161 -0
- polyfile/kaitai/parsers/gettext_mo.py +541 -0
- polyfile/kaitai/parsers/gif.py +492 -0
- polyfile/kaitai/parsers/gimp_brush.py +244 -0
- polyfile/kaitai/parsers/glibc_utmp.py +114 -0
- polyfile/kaitai/parsers/gltf_binary.py +132 -0
- polyfile/kaitai/parsers/google_protobuf.py +151 -0
- polyfile/kaitai/parsers/gpt_partition_table.py +175 -0
- polyfile/kaitai/parsers/gran_turismo_vol.py +140 -0
- polyfile/kaitai/parsers/grub2_font.py +337 -0
- polyfile/kaitai/parsers/gzip.py +232 -0
- polyfile/kaitai/parsers/hashcat_restore.py +60 -0
- polyfile/kaitai/parsers/hccap.py +111 -0
- polyfile/kaitai/parsers/hccapx.py +103 -0
- polyfile/kaitai/parsers/heaps_pak.py +177 -0
- polyfile/kaitai/parsers/heroes_of_might_and_magic_agg.py +116 -0
- polyfile/kaitai/parsers/heroes_of_might_and_magic_bmp.py +34 -0
- polyfile/kaitai/parsers/icmp_packet.py +136 -0
- polyfile/kaitai/parsers/ico.py +129 -0
- polyfile/kaitai/parsers/id3v1_1.py +220 -0
- polyfile/kaitai/parsers/id3v2_3.py +324 -0
- polyfile/kaitai/parsers/id3v2_4.py +423 -0
- polyfile/kaitai/parsers/ines.py +282 -0
- polyfile/kaitai/parsers/ipv4_packet.py +158 -0
- polyfile/kaitai/parsers/ipv6_packet.py +55 -0
- polyfile/kaitai/parsers/iso9660.py +544 -0
- polyfile/kaitai/parsers/java_class.py +1113 -0
- polyfile/kaitai/parsers/jpeg.py +361 -0
- polyfile/kaitai/parsers/luks.py +149 -0
- polyfile/kaitai/parsers/lzh.py +165 -0
- polyfile/kaitai/parsers/mac_os_resource_snd.py +493 -0
- polyfile/kaitai/parsers/mach_o.py +3033 -0
- polyfile/kaitai/parsers/mach_o_fat.py +92 -0
- polyfile/kaitai/parsers/magicavoxel_vox.py +391 -0
- polyfile/kaitai/parsers/manifest.json +1 -0
- polyfile/kaitai/parsers/mbr_partition_table.py +119 -0
- polyfile/kaitai/parsers/mcap.py +1015 -0
- polyfile/kaitai/parsers/microsoft_cfb.py +293 -0
- polyfile/kaitai/parsers/microsoft_network_monitor_v2.py +309 -0
- polyfile/kaitai/parsers/microsoft_pe.py +765 -0
- polyfile/kaitai/parsers/mifare_classic.py +706 -0
- polyfile/kaitai/parsers/minecraft_nbt.py +449 -0
- polyfile/kaitai/parsers/monomakh_sapr_chg.py +69 -0
- polyfile/kaitai/parsers/mozilla_mar.py +239 -0
- polyfile/kaitai/parsers/mp4.py +333 -0
- polyfile/kaitai/parsers/msgpack.py +467 -0
- polyfile/kaitai/parsers/nitf.py +1189 -0
- polyfile/kaitai/parsers/nt_mdt_pal.py +155 -0
- polyfile/kaitai/parsers/ogg.py +118 -0
- polyfile/kaitai/parsers/openpgp_message.py +993 -0
- polyfile/kaitai/parsers/packet_ppi.py +515 -0
- polyfile/kaitai/parsers/pcap.py +344 -0
- polyfile/kaitai/parsers/pcf_font.py +506 -0
- polyfile/kaitai/parsers/pcx.py +195 -0
- polyfile/kaitai/parsers/pcx_dcx.py +79 -0
- polyfile/kaitai/parsers/phar_without_stub.py +399 -0
- polyfile/kaitai/parsers/php_serialized_value.py +505 -0
- polyfile/kaitai/parsers/png.py +721 -0
- polyfile/kaitai/parsers/protocol_body.py +260 -0
- polyfile/kaitai/parsers/psx_tim.py +104 -0
- polyfile/kaitai/parsers/python_pickle.py +718 -0
- polyfile/kaitai/parsers/python_pyc_27.py +510 -0
- polyfile/kaitai/parsers/quake_mdl.py +441 -0
- polyfile/kaitai/parsers/quake_pak.py +112 -0
- polyfile/kaitai/parsers/quicktime_mov.py +634 -0
- polyfile/kaitai/parsers/rar.py +265 -0
- polyfile/kaitai/parsers/regf.py +569 -0
- polyfile/kaitai/parsers/renderware_binary_stream.py +877 -0
- polyfile/kaitai/parsers/resource_fork.py +611 -0
- polyfile/kaitai/parsers/respack.py +57 -0
- polyfile/kaitai/parsers/riff.py +409 -0
- polyfile/kaitai/parsers/rpm.py +964 -0
- polyfile/kaitai/parsers/rtcp_payload.py +579 -0
- polyfile/kaitai/parsers/rtp_packet.py +150 -0
- polyfile/kaitai/parsers/rtpdump.py +115 -0
- polyfile/kaitai/parsers/ruby_marshal.py +423 -0
- polyfile/kaitai/parsers/s3m.py +493 -0
- polyfile/kaitai/parsers/saints_row_2_vpp_pc.py +254 -0
- polyfile/kaitai/parsers/shapefile_index.py +174 -0
- polyfile/kaitai/parsers/shapefile_main.py +893 -0
- polyfile/kaitai/parsers/some_ip.py +209 -0
- polyfile/kaitai/parsers/some_ip_container.py +37 -0
- polyfile/kaitai/parsers/some_ip_sd.py +86 -0
- polyfile/kaitai/parsers/some_ip_sd_entries.py +160 -0
- polyfile/kaitai/parsers/some_ip_sd_options.py +374 -0
- polyfile/kaitai/parsers/specpr.py +404 -0
- polyfile/kaitai/parsers/sqlite3.py +472 -0
- polyfile/kaitai/parsers/ssh_public_key.py +252 -0
- polyfile/kaitai/parsers/standard_midi_file.py +390 -0
- polyfile/kaitai/parsers/stl.py +111 -0
- polyfile/kaitai/parsers/sudoers_ts.py +201 -0
- polyfile/kaitai/parsers/swf.py +406 -0
- polyfile/kaitai/parsers/systemd_journal.py +361 -0
- polyfile/kaitai/parsers/tcp_segment.py +57 -0
- polyfile/kaitai/parsers/tga.py +213 -0
- polyfile/kaitai/parsers/tls_client_hello.py +293 -0
- polyfile/kaitai/parsers/tr_dos_image.py +322 -0
- polyfile/kaitai/parsers/tsm.py +198 -0
- polyfile/kaitai/parsers/ttf.py +1847 -0
- polyfile/kaitai/parsers/udp_datagram.py +42 -0
- polyfile/kaitai/parsers/uefi_te.py +236 -0
- polyfile/kaitai/parsers/uimage.py +198 -0
- polyfile/kaitai/parsers/utf8_string.py +137 -0
- polyfile/kaitai/parsers/vfat.py +410 -0
- polyfile/kaitai/parsers/vlq_base128_be.py +104 -0
- polyfile/kaitai/parsers/vlq_base128_le.py +129 -0
- polyfile/kaitai/parsers/vmware_vmdk.py +167 -0
- polyfile/kaitai/parsers/vp8_ivf.py +112 -0
- polyfile/kaitai/parsers/warcraft_2_pud.py +423 -0
- polyfile/kaitai/parsers/wav.py +1014 -0
- polyfile/kaitai/parsers/websocket.py +167 -0
- polyfile/kaitai/parsers/windows_evt_log.py +304 -0
- polyfile/kaitai/parsers/windows_lnk_file.py +467 -0
- polyfile/kaitai/parsers/windows_minidump.py +575 -0
- polyfile/kaitai/parsers/windows_resource_file.py +243 -0
- polyfile/kaitai/parsers/windows_shell_items.py +190 -0
- polyfile/kaitai/parsers/windows_systemtime.py +52 -0
- polyfile/kaitai/parsers/wmf.py +502 -0
- polyfile/kaitai/parsers/xar.py +181 -0
- polyfile/kaitai/parsers/xwd.py +189 -0
- polyfile/kaitai/parsers/zip.py +685 -0
- polyfile/kaitai/parsers/zisofs.py +158 -0
- polyfile/kaitai/parsers/zx_spectrum_tap.py +184 -0
- polyfile/kaitaimatcher.py +113 -0
- polyfile/languagematcher.py +217 -0
- polyfile/logger.py +135 -0
- polyfile/magic.py +2983 -0
- polyfile/magic_defs/COPYING +29 -0
- polyfile/magic_defs/__init__.py +0 -0
- polyfile/magic_defs/acorn +102 -0
- polyfile/magic_defs/adi +13 -0
- polyfile/magic_defs/adventure +122 -0
- polyfile/magic_defs/aes +29 -0
- polyfile/magic_defs/algol68 +35 -0
- polyfile/magic_defs/allegro +9 -0
- polyfile/magic_defs/alliant +18 -0
- polyfile/magic_defs/alpha +32 -0
- polyfile/magic_defs/amanda +12 -0
- polyfile/magic_defs/amigaos +218 -0
- polyfile/magic_defs/android +259 -0
- polyfile/magic_defs/animation +1197 -0
- polyfile/magic_defs/aout +46 -0
- polyfile/magic_defs/apache +28 -0
- polyfile/magic_defs/apl +7 -0
- polyfile/magic_defs/apple +773 -0
- polyfile/magic_defs/application +7 -0
- polyfile/magic_defs/applix +13 -0
- polyfile/magic_defs/apt +52 -0
- polyfile/magic_defs/archive +2586 -0
- polyfile/magic_defs/aria +38 -0
- polyfile/magic_defs/arm +50 -0
- polyfile/magic_defs/asf +132 -0
- polyfile/magic_defs/assembler +18 -0
- polyfile/magic_defs/asterix +18 -0
- polyfile/magic_defs/att3b +41 -0
- polyfile/magic_defs/audio +1291 -0
- polyfile/magic_defs/avm +33 -0
- polyfile/magic_defs/basis +18 -0
- polyfile/magic_defs/beetle +7 -0
- polyfile/magic_defs/ber +65 -0
- polyfile/magic_defs/bflt +14 -0
- polyfile/magic_defs/bhl +10 -0
- polyfile/magic_defs/bioinformatics +178 -0
- polyfile/magic_defs/biosig +154 -0
- polyfile/magic_defs/blackberry +8 -0
- polyfile/magic_defs/blcr +25 -0
- polyfile/magic_defs/blender +50 -0
- polyfile/magic_defs/blit +24 -0
- polyfile/magic_defs/bm +10 -0
- polyfile/magic_defs/bout +11 -0
- polyfile/magic_defs/bsdi +33 -0
- polyfile/magic_defs/bsi +10 -0
- polyfile/magic_defs/btsnoop +13 -0
- polyfile/magic_defs/burp +7 -0
- polyfile/magic_defs/bytecode +41 -0
- polyfile/magic_defs/c-lang +110 -0
- polyfile/magic_defs/c64 +531 -0
- polyfile/magic_defs/cad +437 -0
- polyfile/magic_defs/cafebabe +107 -0
- polyfile/magic_defs/cbor +21 -0
- polyfile/magic_defs/ccf +14 -0
- polyfile/magic_defs/cddb +12 -0
- polyfile/magic_defs/chord +15 -0
- polyfile/magic_defs/cisco +12 -0
- polyfile/magic_defs/citrus +12 -0
- polyfile/magic_defs/clarion +27 -0
- polyfile/magic_defs/claris +48 -0
- polyfile/magic_defs/clipper +65 -0
- polyfile/magic_defs/clojure +30 -0
- polyfile/magic_defs/coff +98 -0
- polyfile/magic_defs/commands +201 -0
- polyfile/magic_defs/communications +22 -0
- polyfile/magic_defs/compress +461 -0
- polyfile/magic_defs/console +1213 -0
- polyfile/magic_defs/convex +69 -0
- polyfile/magic_defs/coverage +91 -0
- polyfile/magic_defs/cracklib +14 -0
- polyfile/magic_defs/crypto +31 -0
- polyfile/magic_defs/csv +8 -0
- polyfile/magic_defs/ctags +6 -0
- polyfile/magic_defs/ctf +23 -0
- polyfile/magic_defs/cubemap +8 -0
- polyfile/magic_defs/cups +56 -0
- polyfile/magic_defs/dact +11 -0
- polyfile/magic_defs/database +886 -0
- polyfile/magic_defs/dataone +47 -0
- polyfile/magic_defs/dbpf +15 -0
- polyfile/magic_defs/der +146 -0
- polyfile/magic_defs/diamond +12 -0
- polyfile/magic_defs/dif +33 -0
- polyfile/magic_defs/diff +41 -0
- polyfile/magic_defs/digital +59 -0
- polyfile/magic_defs/dolby +69 -0
- polyfile/magic_defs/dsf +25 -0
- polyfile/magic_defs/dump +96 -0
- polyfile/magic_defs/dwarfs +45 -0
- polyfile/magic_defs/dyadic +61 -0
- polyfile/magic_defs/ebml +8 -0
- polyfile/magic_defs/edid +11 -0
- polyfile/magic_defs/editors +43 -0
- polyfile/magic_defs/efi +15 -0
- polyfile/magic_defs/elf +379 -0
- polyfile/magic_defs/encore +22 -0
- polyfile/magic_defs/epoc +62 -0
- polyfile/magic_defs/erlang +21 -0
- polyfile/magic_defs/espressif +57 -0
- polyfile/magic_defs/esri +28 -0
- polyfile/magic_defs/etf +33 -0
- polyfile/magic_defs/fcs +9 -0
- polyfile/magic_defs/filesystems +2694 -0
- polyfile/magic_defs/finger +16 -0
- polyfile/magic_defs/firmware +133 -0
- polyfile/magic_defs/flash +62 -0
- polyfile/magic_defs/flif +36 -0
- polyfile/magic_defs/fonts +449 -0
- polyfile/magic_defs/forth +82 -0
- polyfile/magic_defs/fortran +9 -0
- polyfile/magic_defs/frame +62 -0
- polyfile/magic_defs/freebsd +164 -0
- polyfile/magic_defs/fsav +128 -0
- polyfile/magic_defs/fusecompress +12 -0
- polyfile/magic_defs/games +696 -0
- polyfile/magic_defs/gcc +17 -0
- polyfile/magic_defs/gconv +10 -0
- polyfile/magic_defs/gentoo +85 -0
- polyfile/magic_defs/geo +166 -0
- polyfile/magic_defs/geos +20 -0
- polyfile/magic_defs/gimp +77 -0
- polyfile/magic_defs/git +13 -0
- polyfile/magic_defs/glibc +21 -0
- polyfile/magic_defs/gnome +59 -0
- polyfile/magic_defs/gnu +173 -0
- polyfile/magic_defs/gnumeric +8 -0
- polyfile/magic_defs/gpt +240 -0
- polyfile/magic_defs/gpu +28 -0
- polyfile/magic_defs/grace +21 -0
- polyfile/magic_defs/graphviz +12 -0
- polyfile/magic_defs/gringotts +48 -0
- polyfile/magic_defs/guile +13 -0
- polyfile/magic_defs/hardware +12 -0
- polyfile/magic_defs/hitachi-sh +30 -0
- polyfile/magic_defs/hp +433 -0
- polyfile/magic_defs/human68k +26 -0
- polyfile/magic_defs/ibm370 +52 -0
- polyfile/magic_defs/ibm6000 +35 -0
- polyfile/magic_defs/icc +214 -0
- polyfile/magic_defs/iff +80 -0
- polyfile/magic_defs/images +4210 -0
- polyfile/magic_defs/inform +9 -0
- polyfile/magic_defs/intel +310 -0
- polyfile/magic_defs/interleaf +9 -0
- polyfile/magic_defs/island +10 -0
- polyfile/magic_defs/ispell +63 -0
- polyfile/magic_defs/isz +15 -0
- polyfile/magic_defs/java +52 -0
- polyfile/magic_defs/javascript +171 -0
- polyfile/magic_defs/jpeg +252 -0
- polyfile/magic_defs/json +8 -0
- polyfile/magic_defs/karma +9 -0
- polyfile/magic_defs/kde +11 -0
- polyfile/magic_defs/keepass +20 -0
- polyfile/magic_defs/kerberos +45 -0
- polyfile/magic_defs/kicad +85 -0
- polyfile/magic_defs/kml +34 -0
- polyfile/magic_defs/lammps +64 -0
- polyfile/magic_defs/lecter +6 -0
- polyfile/magic_defs/lex +12 -0
- polyfile/magic_defs/lif +50 -0
- polyfile/magic_defs/linux +557 -0
- polyfile/magic_defs/lisp +78 -0
- polyfile/magic_defs/llvm +22 -0
- polyfile/magic_defs/locoscript +12 -0
- polyfile/magic_defs/lua +31 -0
- polyfile/magic_defs/luks +126 -0
- polyfile/magic_defs/m4 +11 -0
- polyfile/magic_defs/mach +303 -0
- polyfile/magic_defs/macintosh +505 -0
- polyfile/magic_defs/macos +7 -0
- polyfile/magic_defs/magic +10 -0
- polyfile/magic_defs/magic.mgc +0 -0
- polyfile/magic_defs/mail.news +132 -0
- polyfile/magic_defs/make +21 -0
- polyfile/magic_defs/map +413 -0
- polyfile/magic_defs/maple +109 -0
- polyfile/magic_defs/marc21 +30 -0
- polyfile/magic_defs/mathcad +8 -0
- polyfile/magic_defs/mathematica +188 -0
- polyfile/magic_defs/matroska +17 -0
- polyfile/magic_defs/mcrypt +52 -0
- polyfile/magic_defs/measure +44 -0
- polyfile/magic_defs/mercurial +13 -0
- polyfile/magic_defs/metastore +8 -0
- polyfile/magic_defs/meteorological +53 -0
- polyfile/magic_defs/microfocus +21 -0
- polyfile/magic_defs/mime +9 -0
- polyfile/magic_defs/mips +120 -0
- polyfile/magic_defs/mirage +8 -0
- polyfile/magic_defs/misctools +140 -0
- polyfile/magic_defs/mkid +11 -0
- polyfile/magic_defs/mlssa +8 -0
- polyfile/magic_defs/mmdf +6 -0
- polyfile/magic_defs/modem +92 -0
- polyfile/magic_defs/modulefile +9 -0
- polyfile/magic_defs/motorola +71 -0
- polyfile/magic_defs/mozilla +37 -0
- polyfile/magic_defs/msdos +2304 -0
- polyfile/magic_defs/msooxml +68 -0
- polyfile/magic_defs/msvc +222 -0
- polyfile/magic_defs/msx +309 -0
- polyfile/magic_defs/mup +24 -0
- polyfile/magic_defs/music +17 -0
- polyfile/magic_defs/nasa +7 -0
- polyfile/magic_defs/natinst +24 -0
- polyfile/magic_defs/ncr +49 -0
- polyfile/magic_defs/neko +12 -0
- polyfile/magic_defs/netbsd +251 -0
- polyfile/magic_defs/netscape +26 -0
- polyfile/magic_defs/netware +11 -0
- polyfile/magic_defs/news +13 -0
- polyfile/magic_defs/nifty +202 -0
- polyfile/magic_defs/nim-lang +29 -0
- polyfile/magic_defs/nitpicker +14 -0
- polyfile/magic_defs/numpy +9 -0
- polyfile/magic_defs/oasis +12 -0
- polyfile/magic_defs/ocaml +14 -0
- polyfile/magic_defs/octave +6 -0
- polyfile/magic_defs/ole2compounddocs +760 -0
- polyfile/magic_defs/olf +98 -0
- polyfile/magic_defs/openfst +17 -0
- polyfile/magic_defs/opentimestamps +16 -0
- polyfile/magic_defs/oric +16 -0
- polyfile/magic_defs/os2 +186 -0
- polyfile/magic_defs/os400 +39 -0
- polyfile/magic_defs/os9 +80 -0
- polyfile/magic_defs/osf1 +10 -0
- polyfile/magic_defs/palm +156 -0
- polyfile/magic_defs/parix +13 -0
- polyfile/magic_defs/parrot +22 -0
- polyfile/magic_defs/pascal +39 -0
- polyfile/magic_defs/pbf +11 -0
- polyfile/magic_defs/pbm +8 -0
- polyfile/magic_defs/pc88 +24 -0
- polyfile/magic_defs/pc98 +77 -0
- polyfile/magic_defs/pci_ids +116 -0
- polyfile/magic_defs/pcjr +8 -0
- polyfile/magic_defs/pdf +51 -0
- polyfile/magic_defs/pdp +42 -0
- polyfile/magic_defs/perl +100 -0
- polyfile/magic_defs/pgf +52 -0
- polyfile/magic_defs/pgp +581 -0
- polyfile/magic_defs/pgp-binary-keys +388 -0
- polyfile/magic_defs/pkgadd +7 -0
- polyfile/magic_defs/plan9 +25 -0
- polyfile/magic_defs/playdate +57 -0
- polyfile/magic_defs/plus5 +18 -0
- polyfile/magic_defs/pmem +46 -0
- polyfile/magic_defs/polyfile_zip +5 -0
- polyfile/magic_defs/polyml +23 -0
- polyfile/magic_defs/printer +269 -0
- polyfile/magic_defs/project +10 -0
- polyfile/magic_defs/psdbms +14 -0
- polyfile/magic_defs/psl +14 -0
- polyfile/magic_defs/pulsar +13 -0
- polyfile/magic_defs/puzzle +17 -0
- polyfile/magic_defs/pwsafe +14 -0
- polyfile/magic_defs/pyramid +12 -0
- polyfile/magic_defs/python +305 -0
- polyfile/magic_defs/qt +30 -0
- polyfile/magic_defs/revision +66 -0
- polyfile/magic_defs/riff +840 -0
- polyfile/magic_defs/rinex +44 -0
- polyfile/magic_defs/ringdove +45 -0
- polyfile/magic_defs/rpi +52 -0
- polyfile/magic_defs/rpm +45 -0
- polyfile/magic_defs/rpmsg +7 -0
- polyfile/magic_defs/rst +11 -0
- polyfile/magic_defs/rtf +94 -0
- polyfile/magic_defs/ruby +55 -0
- polyfile/magic_defs/rust +21 -0
- polyfile/magic_defs/sc +7 -0
- polyfile/magic_defs/sccs +24 -0
- polyfile/magic_defs/scientific +144 -0
- polyfile/magic_defs/securitycerts +6 -0
- polyfile/magic_defs/selinux +24 -0
- polyfile/magic_defs/sendmail +37 -0
- polyfile/magic_defs/sequent +42 -0
- polyfile/magic_defs/sereal +35 -0
- polyfile/magic_defs/sgi +144 -0
- polyfile/magic_defs/sgml +161 -0
- polyfile/magic_defs/sharc +23 -0
- polyfile/magic_defs/sinclair +40 -0
- polyfile/magic_defs/sisu +18 -0
- polyfile/magic_defs/sketch +6 -0
- polyfile/magic_defs/smalltalk +25 -0
- polyfile/magic_defs/smile +34 -0
- polyfile/magic_defs/sniffer +482 -0
- polyfile/magic_defs/softquad +40 -0
- polyfile/magic_defs/sosi +40 -0
- polyfile/magic_defs/spec +21 -0
- polyfile/magic_defs/spectrum +184 -0
- polyfile/magic_defs/sql +288 -0
- polyfile/magic_defs/ssh +39 -0
- polyfile/magic_defs/ssl +20 -0
- polyfile/magic_defs/statistics +45 -0
- polyfile/magic_defs/subtitle +38 -0
- polyfile/magic_defs/sun +141 -0
- polyfile/magic_defs/svf +5 -0
- polyfile/magic_defs/sylk +36 -0
- polyfile/magic_defs/symbos +42 -0
- polyfile/magic_defs/sysex +429 -0
- polyfile/magic_defs/tcl +29 -0
- polyfile/magic_defs/teapot +6 -0
- polyfile/magic_defs/terminfo +63 -0
- polyfile/magic_defs/tex +141 -0
- polyfile/magic_defs/tgif +7 -0
- polyfile/magic_defs/ti-8x +239 -0
- polyfile/magic_defs/timezone +42 -0
- polyfile/magic_defs/tplink +95 -0
- polyfile/magic_defs/troff +38 -0
- polyfile/magic_defs/tuxedo +8 -0
- polyfile/magic_defs/typeset +8 -0
- polyfile/magic_defs/uf2 +72 -0
- polyfile/magic_defs/unicode +15 -0
- polyfile/magic_defs/unisig +12 -0
- polyfile/magic_defs/unknown +34 -0
- polyfile/magic_defs/usd +21 -0
- polyfile/magic_defs/uterus +16 -0
- polyfile/magic_defs/uuencode +28 -0
- polyfile/magic_defs/vacuum-cleaner +54 -0
- polyfile/magic_defs/varied.out +46 -0
- polyfile/magic_defs/varied.script +21 -0
- polyfile/magic_defs/vax +32 -0
- polyfile/magic_defs/vicar +17 -0
- polyfile/magic_defs/virtual +307 -0
- polyfile/magic_defs/virtutech +12 -0
- polyfile/magic_defs/visx +32 -0
- polyfile/magic_defs/vms +30 -0
- polyfile/magic_defs/vmware +6 -0
- polyfile/magic_defs/vorbis +155 -0
- polyfile/magic_defs/vxl +14 -0
- polyfile/magic_defs/warc +16 -0
- polyfile/magic_defs/weak +16 -0
- polyfile/magic_defs/web +18 -0
- polyfile/magic_defs/webassembly +17 -0
- polyfile/magic_defs/windows +1811 -0
- polyfile/magic_defs/wireless +7 -0
- polyfile/magic_defs/wordprocessors +630 -0
- polyfile/magic_defs/wsdl +23 -0
- polyfile/magic_defs/x68000 +25 -0
- polyfile/magic_defs/xdelta +13 -0
- polyfile/magic_defs/xenix +106 -0
- polyfile/magic_defs/xilinx +58 -0
- polyfile/magic_defs/xo65 +37 -0
- polyfile/magic_defs/xwindows +43 -0
- polyfile/magic_defs/yara +17 -0
- polyfile/magic_defs/zfs +96 -0
- polyfile/magic_defs/zilog +12 -0
- polyfile/magic_defs/zip +126 -0
- polyfile/magic_defs/zyxel +17 -0
- polyfile/nes.py +144 -0
- polyfile/nitf.py +15 -0
- polyfile/pdf.py +1264 -0
- polyfile/pickles.py +45 -0
- polyfile/polyfile.py +409 -0
- polyfile/profiling.py +115 -0
- polyfile/repl.py +624 -0
- polyfile/search.py +310 -0
- polyfile/serialization.py +323 -0
- polyfile/structmatcher.py +46 -0
- polyfile/structs.py +281 -0
- polyfile/templates/download.js +162 -0
- polyfile/templates/hexdump.css +268 -0
- polyfile/templates/hexdump.js +756 -0
- polyfile/templates/jquery-3.4.1.min.js +2 -0
- polyfile/templates/template.html +119 -0
- polyfile/wildcards.py +62 -0
- polyfile/zipmatcher.py +183 -0
- polyfile_weave-0.5.5.dist-info/METADATA +173 -0
- polyfile_weave-0.5.5.dist-info/RECORD +585 -0
- polyfile_weave-0.5.5.dist-info/WHEEL +5 -0
- polyfile_weave-0.5.5.dist-info/entry_points.txt +2 -0
- polyfile_weave-0.5.5.dist-info/licenses/LICENSE +202 -0
- polyfile_weave-0.5.5.dist-info/top_level.txt +2 -0
- polymerge/__init__.py +1 -0
- polymerge/__main__.py +296 -0
- polymerge/cfg.py +127 -0
- polymerge/polymerge.py +227 -0
- polymerge/polytracker.py +190 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
|
|
2
|
+
|
|
3
|
+
from pkg_resources import parse_version
|
|
4
|
+
import kaitaistruct
|
|
5
|
+
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
|
|
6
|
+
import collections
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if parse_version(kaitaistruct.__version__) < parse_version('0.9'):
|
|
10
|
+
raise Exception("Incompatible Kaitai Struct Python API: 0.9 or later is required, but you have %s" % (kaitaistruct.__version__))
|
|
11
|
+
|
|
12
|
+
class Zisofs(KaitaiStruct):
|
|
13
|
+
"""zisofs is a compression format for files on ISO9660 file system. It has
|
|
14
|
+
limited support across operating systems, mainly Linux kernel. Typically a
|
|
15
|
+
directory tree is first preprocessed by mkzftree (from the zisofs-tools
|
|
16
|
+
package before being turned into an ISO9660 image by mkisofs, genisoimage
|
|
17
|
+
or similar tool. The data is zlib compressed.
|
|
18
|
+
|
|
19
|
+
The specification here describes the structure of a file that has been
|
|
20
|
+
preprocessed by mkzftree, not of a full ISO9660 ziso. Data is not
|
|
21
|
+
decompressed, as blocks with length 0 have a special meaning. Decompression
|
|
22
|
+
and deconstruction of this data should be done outside of Kaitai Struct.
|
|
23
|
+
|
|
24
|
+
.. seealso::
|
|
25
|
+
Source - https://web.archive.org/web/20200612093441/https://dev.lovelyhq.com/libburnia/web/-/wikis/zisofs
|
|
26
|
+
"""
|
|
27
|
+
SEQ_FIELDS = ["header", "block_pointers"]
|
|
28
|
+
def __init__(self, _io, _parent=None, _root=None):
|
|
29
|
+
self._io = _io
|
|
30
|
+
self._parent = _parent
|
|
31
|
+
self._root = _root if _root else self
|
|
32
|
+
self._debug = collections.defaultdict(dict)
|
|
33
|
+
|
|
34
|
+
def _read(self):
|
|
35
|
+
self._debug['header']['start'] = self._io.pos()
|
|
36
|
+
self._raw_header = self._io.read_bytes(16)
|
|
37
|
+
_io__raw_header = KaitaiStream(BytesIO(self._raw_header))
|
|
38
|
+
self.header = Zisofs.Header(_io__raw_header, self, self._root)
|
|
39
|
+
self.header._read()
|
|
40
|
+
self._debug['header']['end'] = self._io.pos()
|
|
41
|
+
self._debug['block_pointers']['start'] = self._io.pos()
|
|
42
|
+
self.block_pointers = [None] * ((self.header.num_blocks + 1))
|
|
43
|
+
for i in range((self.header.num_blocks + 1)):
|
|
44
|
+
if not 'arr' in self._debug['block_pointers']:
|
|
45
|
+
self._debug['block_pointers']['arr'] = []
|
|
46
|
+
self._debug['block_pointers']['arr'].append({'start': self._io.pos()})
|
|
47
|
+
self.block_pointers[i] = self._io.read_u4le()
|
|
48
|
+
self._debug['block_pointers']['arr'][i]['end'] = self._io.pos()
|
|
49
|
+
|
|
50
|
+
self._debug['block_pointers']['end'] = self._io.pos()
|
|
51
|
+
|
|
52
|
+
class Header(KaitaiStruct):
|
|
53
|
+
SEQ_FIELDS = ["magic", "uncompressed_size", "len_header", "block_size_log2", "reserved"]
|
|
54
|
+
def __init__(self, _io, _parent=None, _root=None):
|
|
55
|
+
self._io = _io
|
|
56
|
+
self._parent = _parent
|
|
57
|
+
self._root = _root if _root else self
|
|
58
|
+
self._debug = collections.defaultdict(dict)
|
|
59
|
+
|
|
60
|
+
def _read(self):
|
|
61
|
+
self._debug['magic']['start'] = self._io.pos()
|
|
62
|
+
self.magic = self._io.read_bytes(8)
|
|
63
|
+
self._debug['magic']['end'] = self._io.pos()
|
|
64
|
+
if not self.magic == b"\x37\xE4\x53\x96\xC9\xDB\xD6\x07":
|
|
65
|
+
raise kaitaistruct.ValidationNotEqualError(b"\x37\xE4\x53\x96\xC9\xDB\xD6\x07", self.magic, self._io, u"/types/header/seq/0")
|
|
66
|
+
self._debug['uncompressed_size']['start'] = self._io.pos()
|
|
67
|
+
self.uncompressed_size = self._io.read_u4le()
|
|
68
|
+
self._debug['uncompressed_size']['end'] = self._io.pos()
|
|
69
|
+
self._debug['len_header']['start'] = self._io.pos()
|
|
70
|
+
self.len_header = self._io.read_u1()
|
|
71
|
+
self._debug['len_header']['end'] = self._io.pos()
|
|
72
|
+
if not self.len_header == 4:
|
|
73
|
+
raise kaitaistruct.ValidationNotEqualError(4, self.len_header, self._io, u"/types/header/seq/2")
|
|
74
|
+
self._debug['block_size_log2']['start'] = self._io.pos()
|
|
75
|
+
self.block_size_log2 = self._io.read_u1()
|
|
76
|
+
self._debug['block_size_log2']['end'] = self._io.pos()
|
|
77
|
+
if not ((self.block_size_log2 == 15) or (self.block_size_log2 == 16) or (self.block_size_log2 == 17)) :
|
|
78
|
+
raise kaitaistruct.ValidationNotAnyOfError(self.block_size_log2, self._io, u"/types/header/seq/3")
|
|
79
|
+
self._debug['reserved']['start'] = self._io.pos()
|
|
80
|
+
self.reserved = self._io.read_bytes(2)
|
|
81
|
+
self._debug['reserved']['end'] = self._io.pos()
|
|
82
|
+
if not self.reserved == b"\x00\x00":
|
|
83
|
+
raise kaitaistruct.ValidationNotEqualError(b"\x00\x00", self.reserved, self._io, u"/types/header/seq/4")
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def block_size(self):
|
|
87
|
+
if hasattr(self, '_m_block_size'):
|
|
88
|
+
return self._m_block_size if hasattr(self, '_m_block_size') else None
|
|
89
|
+
|
|
90
|
+
self._m_block_size = (1 << self.block_size_log2)
|
|
91
|
+
return self._m_block_size if hasattr(self, '_m_block_size') else None
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def num_blocks(self):
|
|
95
|
+
"""ceil(uncompressed_size / block_size)."""
|
|
96
|
+
if hasattr(self, '_m_num_blocks'):
|
|
97
|
+
return self._m_num_blocks if hasattr(self, '_m_num_blocks') else None
|
|
98
|
+
|
|
99
|
+
self._m_num_blocks = (self.uncompressed_size // self.block_size + (1 if (self.uncompressed_size % self.block_size) != 0 else 0))
|
|
100
|
+
return self._m_num_blocks if hasattr(self, '_m_num_blocks') else None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Block(KaitaiStruct):
|
|
104
|
+
SEQ_FIELDS = []
|
|
105
|
+
def __init__(self, ofs_start, ofs_end, _io, _parent=None, _root=None):
|
|
106
|
+
self._io = _io
|
|
107
|
+
self._parent = _parent
|
|
108
|
+
self._root = _root if _root else self
|
|
109
|
+
self.ofs_start = ofs_start
|
|
110
|
+
self.ofs_end = ofs_end
|
|
111
|
+
self._debug = collections.defaultdict(dict)
|
|
112
|
+
|
|
113
|
+
def _read(self):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def len_data(self):
|
|
118
|
+
if hasattr(self, '_m_len_data'):
|
|
119
|
+
return self._m_len_data if hasattr(self, '_m_len_data') else None
|
|
120
|
+
|
|
121
|
+
self._m_len_data = (self.ofs_end - self.ofs_start)
|
|
122
|
+
return self._m_len_data if hasattr(self, '_m_len_data') else None
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def data(self):
|
|
126
|
+
if hasattr(self, '_m_data'):
|
|
127
|
+
return self._m_data if hasattr(self, '_m_data') else None
|
|
128
|
+
|
|
129
|
+
io = self._root._io
|
|
130
|
+
_pos = io.pos()
|
|
131
|
+
io.seek(self.ofs_start)
|
|
132
|
+
self._debug['_m_data']['start'] = io.pos()
|
|
133
|
+
self._m_data = io.read_bytes(self.len_data)
|
|
134
|
+
self._debug['_m_data']['end'] = io.pos()
|
|
135
|
+
io.seek(_pos)
|
|
136
|
+
return self._m_data if hasattr(self, '_m_data') else None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def blocks(self):
|
|
141
|
+
if hasattr(self, '_m_blocks'):
|
|
142
|
+
return self._m_blocks if hasattr(self, '_m_blocks') else None
|
|
143
|
+
|
|
144
|
+
self._debug['_m_blocks']['start'] = self._io.pos()
|
|
145
|
+
self._m_blocks = [None] * (self.header.num_blocks)
|
|
146
|
+
for i in range(self.header.num_blocks):
|
|
147
|
+
if not 'arr' in self._debug['_m_blocks']:
|
|
148
|
+
self._debug['_m_blocks']['arr'] = []
|
|
149
|
+
self._debug['_m_blocks']['arr'].append({'start': self._io.pos()})
|
|
150
|
+
_t__m_blocks = Zisofs.Block(self.block_pointers[i], self.block_pointers[(i + 1)], self._io, self, self._root)
|
|
151
|
+
_t__m_blocks._read()
|
|
152
|
+
self._m_blocks[i] = _t__m_blocks
|
|
153
|
+
self._debug['_m_blocks']['arr'][i]['end'] = self._io.pos()
|
|
154
|
+
|
|
155
|
+
self._debug['_m_blocks']['end'] = self._io.pos()
|
|
156
|
+
return self._m_blocks if hasattr(self, '_m_blocks') else None
|
|
157
|
+
|
|
158
|
+
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
|
|
2
|
+
|
|
3
|
+
from pkg_resources import parse_version
|
|
4
|
+
import kaitaistruct
|
|
5
|
+
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
|
|
6
|
+
from enum import Enum
|
|
7
|
+
import collections
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if parse_version(kaitaistruct.__version__) < parse_version('0.9'):
|
|
11
|
+
raise Exception("Incompatible Kaitai Struct Python API: 0.9 or later is required, but you have %s" % (kaitaistruct.__version__))
|
|
12
|
+
|
|
13
|
+
class ZxSpectrumTap(KaitaiStruct):
|
|
14
|
+
"""TAP files are used by emulators of ZX Spectrum computer (released in
|
|
15
|
+
1982 by Sinclair Research). TAP file stores blocks of data as if
|
|
16
|
+
they are written to magnetic tape, which was used as primary media
|
|
17
|
+
for ZX Spectrum. Contents of this file can be viewed as a very
|
|
18
|
+
simple linear filesystem, storing named files with some basic
|
|
19
|
+
metainformation prepended as a header.
|
|
20
|
+
|
|
21
|
+
.. seealso::
|
|
22
|
+
Source - https://sinclair.wiki.zxnet.co.uk/wiki/TAP_format
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
class FlagEnum(Enum):
|
|
26
|
+
header = 0
|
|
27
|
+
data = 255
|
|
28
|
+
|
|
29
|
+
class HeaderTypeEnum(Enum):
|
|
30
|
+
program = 0
|
|
31
|
+
num_array = 1
|
|
32
|
+
char_array = 2
|
|
33
|
+
bytes = 3
|
|
34
|
+
SEQ_FIELDS = ["blocks"]
|
|
35
|
+
def __init__(self, _io, _parent=None, _root=None):
|
|
36
|
+
self._io = _io
|
|
37
|
+
self._parent = _parent
|
|
38
|
+
self._root = _root if _root else self
|
|
39
|
+
self._debug = collections.defaultdict(dict)
|
|
40
|
+
|
|
41
|
+
def _read(self):
|
|
42
|
+
self._debug['blocks']['start'] = self._io.pos()
|
|
43
|
+
self.blocks = []
|
|
44
|
+
i = 0
|
|
45
|
+
while not self._io.is_eof():
|
|
46
|
+
if not 'arr' in self._debug['blocks']:
|
|
47
|
+
self._debug['blocks']['arr'] = []
|
|
48
|
+
self._debug['blocks']['arr'].append({'start': self._io.pos()})
|
|
49
|
+
_t_blocks = ZxSpectrumTap.Block(self._io, self, self._root)
|
|
50
|
+
_t_blocks._read()
|
|
51
|
+
self.blocks.append(_t_blocks)
|
|
52
|
+
self._debug['blocks']['arr'][len(self.blocks) - 1]['end'] = self._io.pos()
|
|
53
|
+
i += 1
|
|
54
|
+
|
|
55
|
+
self._debug['blocks']['end'] = self._io.pos()
|
|
56
|
+
|
|
57
|
+
class Block(KaitaiStruct):
|
|
58
|
+
SEQ_FIELDS = ["len_block", "flag", "header", "data", "headerless_data"]
|
|
59
|
+
def __init__(self, _io, _parent=None, _root=None):
|
|
60
|
+
self._io = _io
|
|
61
|
+
self._parent = _parent
|
|
62
|
+
self._root = _root if _root else self
|
|
63
|
+
self._debug = collections.defaultdict(dict)
|
|
64
|
+
|
|
65
|
+
def _read(self):
|
|
66
|
+
self._debug['len_block']['start'] = self._io.pos()
|
|
67
|
+
self.len_block = self._io.read_u2le()
|
|
68
|
+
self._debug['len_block']['end'] = self._io.pos()
|
|
69
|
+
self._debug['flag']['start'] = self._io.pos()
|
|
70
|
+
self.flag = KaitaiStream.resolve_enum(ZxSpectrumTap.FlagEnum, self._io.read_u1())
|
|
71
|
+
self._debug['flag']['end'] = self._io.pos()
|
|
72
|
+
if ((self.len_block == 19) and (self.flag == ZxSpectrumTap.FlagEnum.header)) :
|
|
73
|
+
self._debug['header']['start'] = self._io.pos()
|
|
74
|
+
self.header = ZxSpectrumTap.Header(self._io, self, self._root)
|
|
75
|
+
self.header._read()
|
|
76
|
+
self._debug['header']['end'] = self._io.pos()
|
|
77
|
+
|
|
78
|
+
if self.len_block == 19:
|
|
79
|
+
self._debug['data']['start'] = self._io.pos()
|
|
80
|
+
self.data = self._io.read_bytes((self.header.len_data + 4))
|
|
81
|
+
self._debug['data']['end'] = self._io.pos()
|
|
82
|
+
|
|
83
|
+
if self.flag == ZxSpectrumTap.FlagEnum.data:
|
|
84
|
+
self._debug['headerless_data']['start'] = self._io.pos()
|
|
85
|
+
self.headerless_data = self._io.read_bytes((self.len_block - 1))
|
|
86
|
+
self._debug['headerless_data']['end'] = self._io.pos()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ProgramParams(KaitaiStruct):
|
|
91
|
+
SEQ_FIELDS = ["autostart_line", "len_program"]
|
|
92
|
+
def __init__(self, _io, _parent=None, _root=None):
|
|
93
|
+
self._io = _io
|
|
94
|
+
self._parent = _parent
|
|
95
|
+
self._root = _root if _root else self
|
|
96
|
+
self._debug = collections.defaultdict(dict)
|
|
97
|
+
|
|
98
|
+
def _read(self):
|
|
99
|
+
self._debug['autostart_line']['start'] = self._io.pos()
|
|
100
|
+
self.autostart_line = self._io.read_u2le()
|
|
101
|
+
self._debug['autostart_line']['end'] = self._io.pos()
|
|
102
|
+
self._debug['len_program']['start'] = self._io.pos()
|
|
103
|
+
self.len_program = self._io.read_u2le()
|
|
104
|
+
self._debug['len_program']['end'] = self._io.pos()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class BytesParams(KaitaiStruct):
|
|
108
|
+
SEQ_FIELDS = ["start_address", "reserved"]
|
|
109
|
+
def __init__(self, _io, _parent=None, _root=None):
|
|
110
|
+
self._io = _io
|
|
111
|
+
self._parent = _parent
|
|
112
|
+
self._root = _root if _root else self
|
|
113
|
+
self._debug = collections.defaultdict(dict)
|
|
114
|
+
|
|
115
|
+
def _read(self):
|
|
116
|
+
self._debug['start_address']['start'] = self._io.pos()
|
|
117
|
+
self.start_address = self._io.read_u2le()
|
|
118
|
+
self._debug['start_address']['end'] = self._io.pos()
|
|
119
|
+
self._debug['reserved']['start'] = self._io.pos()
|
|
120
|
+
self.reserved = self._io.read_bytes(2)
|
|
121
|
+
self._debug['reserved']['end'] = self._io.pos()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class Header(KaitaiStruct):
|
|
125
|
+
SEQ_FIELDS = ["header_type", "filename", "len_data", "params", "checksum"]
|
|
126
|
+
def __init__(self, _io, _parent=None, _root=None):
|
|
127
|
+
self._io = _io
|
|
128
|
+
self._parent = _parent
|
|
129
|
+
self._root = _root if _root else self
|
|
130
|
+
self._debug = collections.defaultdict(dict)
|
|
131
|
+
|
|
132
|
+
def _read(self):
|
|
133
|
+
self._debug['header_type']['start'] = self._io.pos()
|
|
134
|
+
self.header_type = KaitaiStream.resolve_enum(ZxSpectrumTap.HeaderTypeEnum, self._io.read_u1())
|
|
135
|
+
self._debug['header_type']['end'] = self._io.pos()
|
|
136
|
+
self._debug['filename']['start'] = self._io.pos()
|
|
137
|
+
self.filename = KaitaiStream.bytes_strip_right(self._io.read_bytes(10), 32)
|
|
138
|
+
self._debug['filename']['end'] = self._io.pos()
|
|
139
|
+
self._debug['len_data']['start'] = self._io.pos()
|
|
140
|
+
self.len_data = self._io.read_u2le()
|
|
141
|
+
self._debug['len_data']['end'] = self._io.pos()
|
|
142
|
+
self._debug['params']['start'] = self._io.pos()
|
|
143
|
+
_on = self.header_type
|
|
144
|
+
if _on == ZxSpectrumTap.HeaderTypeEnum.program:
|
|
145
|
+
self.params = ZxSpectrumTap.ProgramParams(self._io, self, self._root)
|
|
146
|
+
self.params._read()
|
|
147
|
+
elif _on == ZxSpectrumTap.HeaderTypeEnum.num_array:
|
|
148
|
+
self.params = ZxSpectrumTap.ArrayParams(self._io, self, self._root)
|
|
149
|
+
self.params._read()
|
|
150
|
+
elif _on == ZxSpectrumTap.HeaderTypeEnum.char_array:
|
|
151
|
+
self.params = ZxSpectrumTap.ArrayParams(self._io, self, self._root)
|
|
152
|
+
self.params._read()
|
|
153
|
+
elif _on == ZxSpectrumTap.HeaderTypeEnum.bytes:
|
|
154
|
+
self.params = ZxSpectrumTap.BytesParams(self._io, self, self._root)
|
|
155
|
+
self.params._read()
|
|
156
|
+
self._debug['params']['end'] = self._io.pos()
|
|
157
|
+
self._debug['checksum']['start'] = self._io.pos()
|
|
158
|
+
self.checksum = self._io.read_u1()
|
|
159
|
+
self._debug['checksum']['end'] = self._io.pos()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class ArrayParams(KaitaiStruct):
|
|
163
|
+
SEQ_FIELDS = ["reserved", "var_name", "reserved1"]
|
|
164
|
+
def __init__(self, _io, _parent=None, _root=None):
|
|
165
|
+
self._io = _io
|
|
166
|
+
self._parent = _parent
|
|
167
|
+
self._root = _root if _root else self
|
|
168
|
+
self._debug = collections.defaultdict(dict)
|
|
169
|
+
|
|
170
|
+
def _read(self):
|
|
171
|
+
self._debug['reserved']['start'] = self._io.pos()
|
|
172
|
+
self.reserved = self._io.read_u1()
|
|
173
|
+
self._debug['reserved']['end'] = self._io.pos()
|
|
174
|
+
self._debug['var_name']['start'] = self._io.pos()
|
|
175
|
+
self.var_name = self._io.read_u1()
|
|
176
|
+
self._debug['var_name']['end'] = self._io.pos()
|
|
177
|
+
self._debug['reserved1']['start'] = self._io.pos()
|
|
178
|
+
self.reserved1 = self._io.read_bytes(2)
|
|
179
|
+
self._debug['reserved1']['end'] = self._io.pos()
|
|
180
|
+
if not self.reserved1 == b"\x00\x80":
|
|
181
|
+
raise kaitaistruct.ValidationNotEqualError(b"\x00\x80", self.reserved1, self._io, u"/types/array_params/seq/2")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import re
|
|
3
|
+
from typing import Dict, Iterator, List, Tuple, Type
|
|
4
|
+
|
|
5
|
+
from kaitaistruct import KaitaiStruct, KaitaiStructError
|
|
6
|
+
|
|
7
|
+
from .kaitai.parser import ASTNode, KaitaiParser, RootNode
|
|
8
|
+
from .kaitai.parsers.gif import Gif
|
|
9
|
+
from .kaitai.parsers.jpeg import Jpeg
|
|
10
|
+
from .kaitai.parsers.png import Png
|
|
11
|
+
from .logger import getStatusLogger
|
|
12
|
+
from .polyfile import register_parser, InvalidMatch, Match, Parser, Submatch
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
log = getStatusLogger(__name__)
|
|
16
|
+
|
|
17
|
+
KAITAI_MIME_MAPPING: Dict[str, str] = {
|
|
18
|
+
"image/gif": "image/gif.ksy",
|
|
19
|
+
"image/png": "image/png.ksy",
|
|
20
|
+
"image/jpeg": "image/jpeg.ksy",
|
|
21
|
+
"image/vnd.microsoft.icon": "image/ico.ksy",
|
|
22
|
+
# "image/wmf": "image/wmf.ksy", # there is currently a problem with this parser in Python
|
|
23
|
+
"application/vnd.nitf": "image/nitf.ksy",
|
|
24
|
+
"application/vnd.tcpdump.pcap": "network/pcap.ksy",
|
|
25
|
+
"application/x-sqlite3": "database/sqlite3.ksy",
|
|
26
|
+
"application/x-rar": "archive/rar.ksy",
|
|
27
|
+
"font/sfnt": "font/ttf.ksy",
|
|
28
|
+
"application/x-pie-executable": "executable/elf.ksy",
|
|
29
|
+
"application/gzip": "archive/gzip.ksy",
|
|
30
|
+
"application/x-xar": "archive/xar.ksy",
|
|
31
|
+
"application/x-python-code": "executable/python_pyc_27.ksy",
|
|
32
|
+
"application/x-shockwave-flash": "executable/swf.ksy",
|
|
33
|
+
"application/x-doom": "game/doom_wad.ksy",
|
|
34
|
+
"image/x-dcx": "image/pcx_dcx.ksy",
|
|
35
|
+
"model/gltf-binary": "3d/gltf_binary.ksy",
|
|
36
|
+
"application/x-rpm": "archive/rpm.ksy",
|
|
37
|
+
"application/x-cpio": "archive/cpio_old_le.ksy",
|
|
38
|
+
"image/x-gimp-gbr": "image/gimp_brush.ksy",
|
|
39
|
+
# "application/dicom": "image/dicom.ksy", # there is currently a problem with this parser in Python
|
|
40
|
+
"image/bmp": "image/bmp.ksy",
|
|
41
|
+
"application/x-blender": "media/blender_blend.ksy",
|
|
42
|
+
"audio/x-voc": "media/creative_voice_file.ksy",
|
|
43
|
+
"audio/midi": "media/standard_midi_file.ksy",
|
|
44
|
+
"application/dime": "network/dime_message.ksy",
|
|
45
|
+
"application/bson": "serialization/bson.ksy",
|
|
46
|
+
"application/x-ms-shortcut": "windows/windows_lnk_file.ksy",
|
|
47
|
+
"application/x-java-applet": "executable/java_class.ksy",
|
|
48
|
+
# Uncomment this when/if Kaitai fixes its upstream compilation bug for ICC
|
|
49
|
+
# (https://github.com/kaitai-io/kaitai_struct_formats/issues/347#ref-commit-fde2866)
|
|
50
|
+
# "application/vnd.iccprofile": "image/icc_4.ksy"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
IMAGE_MIMETYPES = {
|
|
54
|
+
"image/gif",
|
|
55
|
+
"image/jpeg",
|
|
56
|
+
"image/png",
|
|
57
|
+
"image/bmp",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
MIME_BY_PARSER: Dict[Type[KaitaiStruct], str] = {}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def ast_to_matches(ast: RootNode, parent: Match) -> Iterator[Submatch]:
|
|
64
|
+
stack: List[Tuple[Match, ASTNode]] = [(parent, ast)]
|
|
65
|
+
while stack:
|
|
66
|
+
parent, node = stack.pop()
|
|
67
|
+
|
|
68
|
+
new_node = Submatch(
|
|
69
|
+
name=node.name,
|
|
70
|
+
match_obj=node.raw_value,
|
|
71
|
+
relative_offset=node.start - parent.offset,
|
|
72
|
+
length=len(node.segment),
|
|
73
|
+
parent=parent
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if node is ast and node.obj.__class__ in MIME_BY_PARSER: # type: ignore
|
|
77
|
+
mtype = MIME_BY_PARSER[node.obj.__class__] # type: ignore
|
|
78
|
+
if mtype in IMAGE_MIMETYPES: # type: ignore
|
|
79
|
+
# this is an image type, so create a preview
|
|
80
|
+
new_node.img_data = f"data:{mtype};base64,{base64.b64encode(ast.raw_value).decode('utf-8')}"
|
|
81
|
+
|
|
82
|
+
yield new_node
|
|
83
|
+
stack.extend(reversed([(new_node, c) for c in node.children]))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
for mimetype, kaitai_path in KAITAI_MIME_MAPPING.items():
|
|
87
|
+
class parse_:
|
|
88
|
+
kaitai_parser = KaitaiParser.load(kaitai_path)
|
|
89
|
+
|
|
90
|
+
def __call__(self, stream, match):
|
|
91
|
+
try:
|
|
92
|
+
ast = self.kaitai_parser.parse(stream).ast
|
|
93
|
+
except KaitaiStructError as e:
|
|
94
|
+
log.warning(f"Error parsing {stream.name} using {self.kaitai_parser}: {e!s}")
|
|
95
|
+
raise InvalidMatch()
|
|
96
|
+
except Exception as e:
|
|
97
|
+
log.error(f"Unexpected exception parsing {stream.name} using {self.kaitai_parser}: {e!s}")
|
|
98
|
+
raise InvalidMatch()
|
|
99
|
+
yield from ast_to_matches(ast, parent=match)
|
|
100
|
+
|
|
101
|
+
func_name = mimetype.replace("/", "_").replace("-", "_")
|
|
102
|
+
|
|
103
|
+
parse_.__name__ = f"{parse_.__name__}{func_name}"
|
|
104
|
+
parse_.__qualname__ = f"{parse_.__qualname__}{func_name}"
|
|
105
|
+
|
|
106
|
+
register_parser(mimetype)(parse_())
|
|
107
|
+
|
|
108
|
+
MIME_BY_PARSER[parse_.kaitai_parser.struct_type] = mimetype
|
|
109
|
+
|
|
110
|
+
del func_name
|
|
111
|
+
del kaitai_path
|
|
112
|
+
del mimetype
|
|
113
|
+
del parse_
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import List, Optional, Sequence
|
|
3
|
+
|
|
4
|
+
from .logger import StatusLogger
|
|
5
|
+
from .polyfile import Match, Submatch, register_parser
|
|
6
|
+
from .magic import AbsoluteOffset, FailedTest, MagicMatcher, MagicTest, MatchedTest, TestResult, TestType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
log = StatusLogger("polyfile")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BFCommandType(Enum):
|
|
13
|
+
LOOP_START = "["
|
|
14
|
+
LOOP_END = "]"
|
|
15
|
+
INPUT = ","
|
|
16
|
+
PRINT = "."
|
|
17
|
+
SHIFT_RIGHT = ">"
|
|
18
|
+
SHIFT_LEFT = "<"
|
|
19
|
+
INCREMENT = "+"
|
|
20
|
+
DECREMENT = "-"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BFCommand:
|
|
24
|
+
def __init__(self, command_type: BFCommandType, offset: int):
|
|
25
|
+
self.command_type: BFCommandType = command_type
|
|
26
|
+
self.offset: int = offset
|
|
27
|
+
|
|
28
|
+
def __str__(self):
|
|
29
|
+
return self.command_type.value
|
|
30
|
+
|
|
31
|
+
def __repr__(self):
|
|
32
|
+
return f"{self.__class__.__name__}(command_type={self.command_type!r}, offset={self.offset!r})"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BFParseError(RuntimeError):
|
|
36
|
+
def __init__(self, message: str, offset: int):
|
|
37
|
+
super().__init__(message)
|
|
38
|
+
self.offset: int = offset
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class BFProgram:
|
|
42
|
+
def __init__(self, commands: Sequence[BFCommand]):
|
|
43
|
+
self.commands: Sequence[BFCommand] = commands
|
|
44
|
+
self._num_loops: Optional[int] = None
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def num_loops(self) -> int:
|
|
48
|
+
if self._num_loops is None:
|
|
49
|
+
self._num_loops = sum(1 for c in self.commands if c.command_type == BFCommandType.LOOP_START)
|
|
50
|
+
return self._num_loops
|
|
51
|
+
|
|
52
|
+
def __bytes__(self):
|
|
53
|
+
return bytes(bytearray(
|
|
54
|
+
[ord(c.command_type.value) for c in self.commands]
|
|
55
|
+
))
|
|
56
|
+
|
|
57
|
+
def __str__(self):
|
|
58
|
+
ret = []
|
|
59
|
+
indent = 0
|
|
60
|
+
for c in self.commands:
|
|
61
|
+
command_type = c.command_type
|
|
62
|
+
if command_type == BFCommandType.LOOP_START:
|
|
63
|
+
indent_str = " " * indent
|
|
64
|
+
ret.append(f"\n{indent_str}[\n{indent_str}{indent_str}")
|
|
65
|
+
indent += 1
|
|
66
|
+
elif command_type == BFCommandType.LOOP_END:
|
|
67
|
+
indent -= 1
|
|
68
|
+
indent_str = " " * indent
|
|
69
|
+
ret.append(f"\n{indent_str}]\n{indent_str}")
|
|
70
|
+
else:
|
|
71
|
+
ret.append(command_type.value)
|
|
72
|
+
return "".join(ret)
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def parse(cls, data: bytes) -> "BFProgram":
|
|
76
|
+
opened = 0
|
|
77
|
+
offset = 0
|
|
78
|
+
commands: List[BFCommand] = []
|
|
79
|
+
for offset, c in enumerate(data):
|
|
80
|
+
try:
|
|
81
|
+
command = BFCommand(BFCommandType(chr(c)), offset)
|
|
82
|
+
except ValueError:
|
|
83
|
+
continue
|
|
84
|
+
commands.append(command)
|
|
85
|
+
if command.command_type == BFCommandType.LOOP_START:
|
|
86
|
+
opened += 1
|
|
87
|
+
elif command.command_type == BFCommandType.LOOP_END:
|
|
88
|
+
opened -= 1
|
|
89
|
+
if opened < 0:
|
|
90
|
+
break
|
|
91
|
+
if opened != 0:
|
|
92
|
+
raise BFParseError("unbalanced square brackets", offset=offset)
|
|
93
|
+
return BFProgram(tuple(commands))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class BFMatcher(MagicTest):
|
|
97
|
+
min_bf_commands: int = 24
|
|
98
|
+
"""The minimum number of BF commands that must appear for the file to be classified as a BF program
|
|
99
|
+
|
|
100
|
+
This is an arbitrary value; the default is high enough to eliminate most false-positives, at the expense of missing
|
|
101
|
+
the detection of shorter BrainFu programs.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
min_bf_loops: int = 2
|
|
105
|
+
"""The minimum number of BF loops that must appear for the file to be classified as a BF program
|
|
106
|
+
|
|
107
|
+
This is an arbitrary value that we have set high enough to eliminate most false-positives, at the expense of missing
|
|
108
|
+
the detection of simpler BrainFu programs.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
reject_arithmetical_noops: bool = False
|
|
112
|
+
"""If True, then reject any BF program that contains arithmetic that cancels itself
|
|
113
|
+
|
|
114
|
+
For example: `-+` or `+-`
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self):
|
|
118
|
+
super().__init__(
|
|
119
|
+
offset=AbsoluteOffset(0),
|
|
120
|
+
mime="application/x-brainfuck",
|
|
121
|
+
extensions=("bf",),
|
|
122
|
+
message="Brainfu** Program"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def subtest_type(self) -> TestType:
|
|
126
|
+
# BF can technically contain any arbitrary bytes, which are interpreted as comments,
|
|
127
|
+
# so declare this as a binary test so we don't miss BF polyglots.
|
|
128
|
+
return TestType.BINARY
|
|
129
|
+
|
|
130
|
+
def test(self, data: bytes, absolute_offset: int, parent_match: Optional[TestResult]) -> TestResult:
|
|
131
|
+
try:
|
|
132
|
+
program = BFProgram.parse(data[absolute_offset:])
|
|
133
|
+
except BFParseError as e:
|
|
134
|
+
return FailedTest(self, offset=e.offset, message=str(e))
|
|
135
|
+
important_commands = frozenset(BFCommandType) - {BFCommandType.INPUT}
|
|
136
|
+
unique_commands = {c.command_type for c in program.commands}
|
|
137
|
+
if program.commands:
|
|
138
|
+
first_command = program.commands[0].offset
|
|
139
|
+
last_command = program.commands[-1].offset
|
|
140
|
+
else:
|
|
141
|
+
first_command = 0
|
|
142
|
+
last_command = 0
|
|
143
|
+
if important_commands - unique_commands:
|
|
144
|
+
return FailedTest(self, offset=first_command,
|
|
145
|
+
message=f"missing commands "
|
|
146
|
+
f"{', '.join((cmd.value for cmd in important_commands - unique_commands))}")
|
|
147
|
+
elif self.min_bf_loops > program.num_loops:
|
|
148
|
+
return FailedTest(self, offset=first_command, message=f"expected at least {self.min_bf_loops} BrainFu loops "
|
|
149
|
+
f"but only found {program.num_loops}")
|
|
150
|
+
elif self.min_bf_commands > len(program.commands):
|
|
151
|
+
return FailedTest(self, offset=first_command, message=f"expected at least {self.min_bf_commands} BrainFu "
|
|
152
|
+
f"commands but only found {len(program.commands)}")
|
|
153
|
+
elif self.reject_arithmetical_noops:
|
|
154
|
+
for c1, c2 in zip(program.commands, program.commands[1:]):
|
|
155
|
+
if c2.offset == c1.offset + 1 and \
|
|
156
|
+
{c1.command_type, c2.command_type} == {BFCommandType.INCREMENT, BFCommandType.DECREMENT}:
|
|
157
|
+
return FailedTest(self, offset=c1.offset, message="BrainFu program contains arithmetic that "
|
|
158
|
+
"cancels itself out")
|
|
159
|
+
else:
|
|
160
|
+
assert 0 <= first_command <= last_command
|
|
161
|
+
return MatchedTest(self, value=bytes(program), offset=first_command,
|
|
162
|
+
length=last_command - first_command)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
MagicMatcher.DEFAULT_INSTANCE.add(BFMatcher())
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@register_parser("application/x-brainfuck")
|
|
169
|
+
def parse_bf(file_stream, match):
|
|
170
|
+
commands = {
|
|
171
|
+
ord('['): "LoopStart",
|
|
172
|
+
ord(']'): "LoopEnd",
|
|
173
|
+
ord('.'): "Print",
|
|
174
|
+
ord('+'): "Increment",
|
|
175
|
+
ord('-'): "Decrement",
|
|
176
|
+
ord('>'): "MoveRight",
|
|
177
|
+
ord('<'): "MoveLeft",
|
|
178
|
+
ord(','): "Input",
|
|
179
|
+
}
|
|
180
|
+
relative_offset = 0
|
|
181
|
+
loop_stack = []
|
|
182
|
+
|
|
183
|
+
while True:
|
|
184
|
+
b = file_stream.read(1)
|
|
185
|
+
if len(b) < 1:
|
|
186
|
+
break
|
|
187
|
+
if b[0] in commands:
|
|
188
|
+
if loop_stack:
|
|
189
|
+
parent: Match = loop_stack[-1]
|
|
190
|
+
else:
|
|
191
|
+
parent = match
|
|
192
|
+
if b == b"[":
|
|
193
|
+
loop = Submatch(
|
|
194
|
+
"Loop",
|
|
195
|
+
match_obj="",
|
|
196
|
+
relative_offset=relative_offset,
|
|
197
|
+
parent=parent,
|
|
198
|
+
matcher=match.matcher
|
|
199
|
+
)
|
|
200
|
+
yield loop
|
|
201
|
+
loop_stack.append(loop)
|
|
202
|
+
parent = loop
|
|
203
|
+
elif b == b"]":
|
|
204
|
+
if not loop_stack:
|
|
205
|
+
log.warning(f"Unexpected closing bracket at offset {relative_offset + match.offset}")
|
|
206
|
+
else:
|
|
207
|
+
loop_stack.pop()
|
|
208
|
+
s = Submatch(
|
|
209
|
+
commands[b[0]],
|
|
210
|
+
match_obj=b,
|
|
211
|
+
relative_offset=relative_offset - (parent.offset - match.offset),
|
|
212
|
+
length=1,
|
|
213
|
+
parent=parent,
|
|
214
|
+
matcher=match.matcher
|
|
215
|
+
)
|
|
216
|
+
yield s
|
|
217
|
+
relative_offset += 1
|