Skip to content

Instantly share code, notes, and snippets.

@endolith
Last active December 3, 2025 16:36
Show Gist options
  • Select an option

  • Save endolith/1119561 to your computer and use it in GitHub Desktop.

Select an option

Save endolith/1119561 to your computer and use it in GitHub Desktop.
Launch ambiguous files in the appropriate program
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

Magic Number File Launcher

This script lets you launch a file with an ambiguous file extension, like .sch, which is used to represent many different formats for many different programs. Windows only lets you associate files based on their extension. This script looks into the file itself and determines what type it is, then launches the correct app, similar to the way Linux looks at magic numbers.

Windows 7 doesn't let you associate with a script directly, but you can do it in the registry. Associate it with something, then regedit, go to HKEY_CLASSES_ROOT\SCH_auto_file\shell\open\command, and add the script like "C:\..python" "..script.py" "%1"

You can put in any bytes you want to make this work with any ambiguous file extension. None of the magic numbers can be a substring of another, or the wrong one might be recognized. Use a hex editor like HxD to open a bunch of files from the same app, and cycle through them looking for the first byte that changes. If you add more formats, please leave them in a comment here or fork it or whatever.

The paths aren't necessarily right. I don't actually have all of these installed right now.

Config file

This is in YAML format. The first indentation level is just a name for the program, then the path to the executable, and then the list of file signatures that it should open

Enclose paths and plain text signatures in single quotes 'like this' to avoid problems with slashes and colons

Enclosing text dumps in double quotes "like this" allows use of C escapes like \r and \n

Hex dumps are automatically recognized as long as each byte is separated by a space, like this: 49 49 02 04. (10 by itself does not get recognized even though it fits the regex because YAML interprets it as an integer before it gets a chance to be regexed. So I had to use the !hexdump tag for that one.)

Some of these file dumps are from personal testing, others from File Extension Seeker

To do

  • I got this compiling with cc_freeze, and moved the configuration into a .yml file, so post that version of the code.
    • Try to make a single executable with bbfreeze instead
  • Handle .pcb and .brd files, too? Then it snowballs into handling any ambiguous extension with one program, though.
  • Use python-magic/libmagic instead?
    • More complex, but more powerful format
    • Have to invent mime types for these schematic/PCB files
    • Could be expanded to many file types, could become a Mini-Magic for Windows for any ambiguous file type? (But same icon for all those files? :/)
  • Use balloon pop-ups instead of opening the Command Prompt window
"""
Created on Mon Aug 01 22:16:44 2011
@author: [email protected]
"""
import binascii
import os.path
import re
import subprocess
import sys
from time import sleep
import yaml
# TODO: This needs to be an absolute path? Should fit into whatever scheme
# Windows uses for that stuff.
# Different on my different computers
# Or there's probably a way to say "in the same directory as this file, not the
# working directory"
# os.path.dirname(os.path.abspath(__file__)) doesn't work after cc_freeze
signatures_file = 'magic_numbers.yml'
# Automatically recognize hexadecimal sequences like 'cf 12 f2 d0 23'
def hex_constructor(loader, node):
""" Converts hex like '88 ce cf c2' into '\x88\xce\xcf\xc2' """
value = loader.construct_scalar(node)
stripped = ''.join(value.split())
return binascii.unhexlify(stripped)
def main():
yaml.add_constructor(u'!hexdump', hex_constructor)
pattern = re.compile(r'^[0-9A-Fa-f]{2}( [0-9A-Fa-f]{2})*$')
yaml.add_implicit_resolver(u'!hexdump', pattern)
try:
magic_numbers = yaml.load(open(signatures_file))
except Exception as e:
print('error reading ' + signatures_file)
sys.exit(e)
# TODO: Check whether definitions conflict. Check every signature against
# every other, minimum of the two sizes
try:
filename = sys.argv[1]
f = open(filename, 'rb')
except IndexError:
sys.exit('Needs a filename to launch')
except IOError:
sys.exit("File not found: " + filename)
# Read just enough bytes to match the signatures
max_len = 0
for value in magic_numbers.values():
max_len = max(max_len, len(max(value['signatures'])))
magic_n = f.read(max_len)
f.close()
program = None
# Compare file with signatures
for file_type, value in magic_numbers.items():
for sig in value['signatures']:
if sig == magic_n[:len(sig)]:
# File signature found
print("File type detected: " + file_type)
program = os.path.expandvars(value['path'])
if program:
print("Opening with: " + program)
else:
sys.exit("No executable listed in " + signatures_file +
" for " + file_type)
break
if program:
break
else:
# File signature not found
print('Unknown file format, starts with:')
# Hex dump
hex_dump = binascii.hexlify(magic_n)
print('\n ' + ' '.join([hex_dump[x:x+2]
for x in range(0, len(hex_dump), 2)]))
# Plain text
print(repr(magic_n))
# With special characters stripped
# print('\n ' + ''.join(c for c in magic_n if ord(c) >= 32))
try:
subprocess.Popen([program, filename])
sleep(3) # So the message is readable
except Exception as e:
print("Error running program: ")
exit(e)
if __name__ == "__main__":
try:
main()
except SystemExit as e:
print(e)
input('(Press <Enter> to close)')
except Exception as e:
print('Error:')
print(e)
# Otherwise Windows closes the window too quickly to read
input('(Press <Enter> to close)')
raise
# Hopefully this config file is pretty self-explanatory. See readme.md if you're confused.
TINA:
path: '%PROGRAMFILES(X86)%\DesignSoft\Tina 9 - TI\TINA.EXE'
signatures:
- 'OBSS'
PSpice Schematics:
path: '%PROGRAMFILES%\OrCAD_Demo\PSpice\psched.exe'
signatures:
- '*version' # OrCad Capture can't open these, only PSpice Schematics can
OrCad Capture:
path: 'C:\OrCAD\OrCAD_16.5_Lite\tools\capture\Capture.exe'
signatures:
- "Schematic FILE\r\n" # OrCad 16.5 says "Translating SDT Schematic to Capture"
- 78 31 30 30 88 CE CF CF 4F 72 43 41 44 # "OrCAD 32bit Schematic" OrCad 16.5 says "Translating SDT Schematic to Capture."
- D0 CF 11 E0 A1 B1 1A E1 # PSpice says "Capture 7.2 data format"
Protel:
path: '%PROGRAMFILES(X86)%\Altium Designer S09 Viewer\dxp.exe'
signatures:
- 'DProtel for Windows'
PADS Logic:
path: 'C:\MentorGraphics\9.3PADS\SDD_HOME\Programs\powerlogic.exe'
signatures:
- 00 FE
- ff a3
Eagle:
path: '%PROGRAMFILES(X86)%\EAGLE-6.1.0\bin\eagle.exe'
signatures:
- !hexdump 10 # Eagle 5?
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE eagle" # Eagle 6
CIRCAD:
path: '%PROGRAMFILES(X86)%\OmniGlyph\OmniGlyph.exe'
signatures:
- 'CIRCAD Version 3' # v3.x
- 'CIRCAD Version 4' # v4.x
- 'Contents: CIRCAD' # v5.x
Qucs:
path: '%PROGRAMFILES(X86)%\Qucs\bin\qucs.exe'
signatures:
- '<Qucs Schematic ' # v0.0.x
P-CAD:
path: # Can be opened by Altium?
signatures:
- 'Personal CAD Sys' # PC-CAPS Database
- 49 49 02 04 20 B4 # P-CAD schematic
# A very simple setup script to create a single executable
#
# hello.py is a very simple "Hello, world" type script which also displays the
# environment in which the script runs
#
# Run the build process by running the command 'python setup.py build'
#
# If everything works well you should find a subdirectory in the build
# subdirectory that contains the files needed to run the script without Python
from cx_Freeze import setup, Executable
Target_1 = Executable(
# what to build
script = "magic_launcher.py",
#initScript = None,
#base = 'Win32GUI',
#targetDir = r"dist",
#targetName = "hello.exe",
compress = False,
copyDependentFiles = True,
appendScriptToExe = True,
appendScriptToLibrary = False,
icon = r'geda win7.ico',
)
setup(
name = "Magic number launcher",
version = "0.1",
description = "SCH file launcher",
author = "[email protected]",
executables = [Target_1],
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment