Initial commit
This commit is contained in:
112
.gitignore
vendored
Normal file
112
.gitignore
vendored
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
mame.json
|
||||||
|
mame/*
|
||||||
|
mfe.ini
|
||||||
|
mame.dat
|
||||||
|
mess.dat
|
||||||
|
mame_exclude.txt
|
||||||
|
|
||||||
|
.vscode/tags
|
||||||
|
|
||||||
|
# Mac Files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# 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/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# 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/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
|
||||||
60
.vscode/launch.json
vendored
Normal file
60
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [{
|
||||||
|
"name": "Python",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"stopOnEntry": false,
|
||||||
|
"pythonPath": "${config:python.pythonPath}",
|
||||||
|
"program": "${workspaceRoot}/mfe.py",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"env": {},
|
||||||
|
"envFile": "${workspaceRoot}/.env",
|
||||||
|
"debugOptions": [
|
||||||
|
"RedirectOutput"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Integrated Terminal/Console",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"stopOnEntry": true,
|
||||||
|
"pythonPath": "${config:python.pythonPath}",
|
||||||
|
"program": "${file}",
|
||||||
|
"cwd": "",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"env": {},
|
||||||
|
"envFile": "${workspaceRoot}/.env",
|
||||||
|
"debugOptions": [
|
||||||
|
"WaitOnAbnormalExit",
|
||||||
|
"WaitOnNormalExit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "External Terminal/Console",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"stopOnEntry": true,
|
||||||
|
"pythonPath": "${config:python.pythonPath}",
|
||||||
|
"program": "${file}",
|
||||||
|
"cwd": "",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"env": {},
|
||||||
|
"envFile": "${workspaceRoot}/.env",
|
||||||
|
"debugOptions": [
|
||||||
|
"WaitOnAbnormalExit",
|
||||||
|
"WaitOnNormalExit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach (Remote Debug)",
|
||||||
|
"type": "python",
|
||||||
|
"request": "attach",
|
||||||
|
"localRoot": "${workspaceRoot}",
|
||||||
|
"remoteRoot": "${workspaceRoot}",
|
||||||
|
"port": 3000,
|
||||||
|
"secret": "my_secret",
|
||||||
|
"host": "localhost"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"python.formatting.provider": "yapf",
|
||||||
|
"python.linting.flake8Enabled": true,
|
||||||
|
"python.linting.flake8Args": ["--ignore", "E501,E722,C0103"],
|
||||||
|
"editor.formatOnPaste": true,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnType": true,
|
||||||
|
"python.pythonPath": "/Users/rich/.local/share/virtualenvs/mfe-K8QhrIKQ/bin/python",
|
||||||
|
}
|
||||||
22
Pipfile
Normal file
22
Pipfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
configobj = ">=5.0.6"
|
||||||
|
appdirs = ">=1.4.3"
|
||||||
|
lxml = ">=3.8.0"
|
||||||
|
titlecase = ">=0.11.0"
|
||||||
|
#pygame = {path = "./../pygame/dist/pygame-1.9.5.dev0-py3.7-macosx-10.14-x86_64.egg"}
|
||||||
|
#Pygame = ">=1.9.3"
|
||||||
|
pygame = {file = "file:///Users/rich/git/pygame/dist/pygame-1.9.5.dev0-cp37-cp37m-macosx_10_14_x86_64.whl"}
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
pylint = "*"
|
||||||
|
flake8 = "*"
|
||||||
|
cx-freeze = {editable = true,git = "https://github.com/anthony-tuininga/cx_Freeze.git"}
|
||||||
|
yapf = "*"
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.7"
|
||||||
210
Pipfile.lock
generated
Normal file
210
Pipfile.lock
generated
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "d7ff85d4507b7eaaf2f2f6433e8450ffcf52bdd74a889e3051a38acd4cd3c62a"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.7"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"appdirs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
|
||||||
|
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.4.3"
|
||||||
|
},
|
||||||
|
"configobj": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==5.0.6"
|
||||||
|
},
|
||||||
|
"lxml": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:02bc220d61f46e9b9d5a53c361ef95e9f5e1d27171cd461dddb17677ae2289a5",
|
||||||
|
"sha256:22f253b542a342755f6cfc047fe4d3a296515cf9b542bc6e261af45a80b8caf6",
|
||||||
|
"sha256:2f31145c7ff665b330919bfa44aacd3a0211a76ca7e7b441039d2a0b0451e415",
|
||||||
|
"sha256:36720698c29e7a9626a0dc802ef8885f8f0239bfd1689628ecd459a061f2807f",
|
||||||
|
"sha256:438a1b0203545521f6616132bfe0f4bca86f8a401364008b30e2b26ec408ce85",
|
||||||
|
"sha256:4815892904c336bbaf73dafd54f45f69f4021c22b5bad7332176bbf4fb830568",
|
||||||
|
"sha256:5be031b0f15ad63910d8e5038b489d95a79929513b3634ad4babf77100602588",
|
||||||
|
"sha256:5c93ae37c3c588e829b037fdfbd64a6e40c901d3f93f7beed6d724c44829a3ad",
|
||||||
|
"sha256:60842230678674cdac4a1cf0f707ef12d75b9a4fc4a565add4f710b5fcf185d5",
|
||||||
|
"sha256:62939a8bb6758d1bf923aa1c13f0bcfa9bf5b2fc0f5fa917a6e25db5fe0cfa4e",
|
||||||
|
"sha256:75830c06a62fe7b8fe3bbb5f269f0b308f19f3949ac81cfd40062f47c1455faf",
|
||||||
|
"sha256:81992565b74332c7c1aff6a913a3e906771aa81c9d0c68c68113cffcae45bc53",
|
||||||
|
"sha256:8c892fb0ee52c594d9a7751c7d7356056a9682674b92cc1c4dc968ff0f30c52f",
|
||||||
|
"sha256:9d862e3cf4fc1f2837dedce9c42269c8c76d027e49820a548ac89fdcee1e361f",
|
||||||
|
"sha256:a623965c086a6e91bb703d4da62dabe59fe88888e82c4117d544e11fd74835d6",
|
||||||
|
"sha256:a7783ab7f6a508b0510490cef9f857b763d796ba7476d9703f89722928d1e113",
|
||||||
|
"sha256:aab09fbe8abfa3b9ce62aaf45aca2d28726b1b9ee44871dbe644050a2fff4940",
|
||||||
|
"sha256:abf181934ac3ef193832fb973fd7f6149b5c531903c2ec0f1220941d73eee601",
|
||||||
|
"sha256:ae07fa0c115733fce1e9da96a3ac3fa24801742ca17e917e0c79d63a01eeb843",
|
||||||
|
"sha256:b9c78242219f674ab645ec571c9a95d70f381319a23911941cd2358a8e0521cf",
|
||||||
|
"sha256:bccb267678b870d9782c3b44d0cefe3ba0e329f9af8c946d32bf3778e7a4f271",
|
||||||
|
"sha256:c4df4d27f4c93b2cef74579f00b1d3a31a929c7d8023f870c4b476f03a274db4",
|
||||||
|
"sha256:caf0e50b546bb60dfa99bb18dfa6748458a83131ecdceaf5c071d74907e7e78a",
|
||||||
|
"sha256:d3266bd3ac59ac4edcd5fa75165dee80b94a3e5c91049df5f7c057ccf097551c",
|
||||||
|
"sha256:db0d213987bcd4e6d41710fb4532b22315b0d8fb439ff901782234456556aed1",
|
||||||
|
"sha256:dbbd5cf7690a40a9f0a9325ab480d0fccf46d16b378eefc08e195d84299bfae1",
|
||||||
|
"sha256:e16e07a0ec3a75b5ee61f2b1003c35696738f937dc8148fbda9fe2147ccb6e61",
|
||||||
|
"sha256:e175a006725c7faadbe69e791877d09936c0ef2cf49d01b60a6c1efcb0e8be6f",
|
||||||
|
"sha256:edd9c13a97f6550f9da2236126bb51c092b3b1ce6187f2bd966533ad794bbb5e",
|
||||||
|
"sha256:fa39ea60d527fbdd94215b5e5552f1c6a912624521093f1384a491a8ad89ad8b"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==4.2.5"
|
||||||
|
},
|
||||||
|
"pygame": {
|
||||||
|
"file": "file:///Users/rich/git/pygame/dist/pygame-1.9.5.dev0-cp37-cp37m-macosx_10_14_x86_64.whl",
|
||||||
|
"hashes": [
|
||||||
|
"sha256:9b811036eebebca68f166683f21427d1682f62159cb6de2b6724dec2be322c2a",
|
||||||
|
"sha256:9fba43308b121628a6c70edc7fac4b07ae5e30ccfe5bf36b914b3a2130e1671a",
|
||||||
|
"sha256:a8e217e5c39c5ea2900f450f2e8acdb97487053a13447f680608200a956e8fe3",
|
||||||
|
"sha256:b48da4cd1b70487791767fcae7e9e3ce8eaf4d340667b5eea14d49a4104e23d1"
|
||||||
|
],
|
||||||
|
"version": "==1.9.5.dev0"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||||
|
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||||
|
],
|
||||||
|
"version": "==1.11.0"
|
||||||
|
},
|
||||||
|
"titlecase": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84de7a97fb702c400e5ba11c6b30849944b39db12e20fbf4515a23c7538a0611",
|
||||||
|
"sha256:95d643a0c08097c02933aced707adfe1c275c335019e8e514dea782a465c5b84"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {
|
||||||
|
"astroid": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:35b032003d6a863f5dcd7ec11abd5cd5893428beaa31ab164982403bcb311f22",
|
||||||
|
"sha256:6a5d668d7dc69110de01cdf7aeec69a679ef486862a0850cc0fd5571505b6b7e"
|
||||||
|
],
|
||||||
|
"version": "==2.1.0"
|
||||||
|
},
|
||||||
|
"cx-freeze": {
|
||||||
|
"editable": true,
|
||||||
|
"git": "https://github.com/anthony-tuininga/cx_Freeze.git",
|
||||||
|
"ref": "9e06b761740a9e93431ee7ea8d0b10f786446a6a"
|
||||||
|
},
|
||||||
|
"flake8": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670",
|
||||||
|
"sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.6.0"
|
||||||
|
},
|
||||||
|
"isort": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af",
|
||||||
|
"sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
|
||||||
|
"sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
|
||||||
|
],
|
||||||
|
"version": "==4.3.4"
|
||||||
|
},
|
||||||
|
"lazy-object-proxy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
|
||||||
|
"sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39",
|
||||||
|
"sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019",
|
||||||
|
"sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088",
|
||||||
|
"sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b",
|
||||||
|
"sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e",
|
||||||
|
"sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6",
|
||||||
|
"sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b",
|
||||||
|
"sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5",
|
||||||
|
"sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff",
|
||||||
|
"sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd",
|
||||||
|
"sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7",
|
||||||
|
"sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff",
|
||||||
|
"sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d",
|
||||||
|
"sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2",
|
||||||
|
"sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35",
|
||||||
|
"sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4",
|
||||||
|
"sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514",
|
||||||
|
"sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252",
|
||||||
|
"sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109",
|
||||||
|
"sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f",
|
||||||
|
"sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c",
|
||||||
|
"sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92",
|
||||||
|
"sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577",
|
||||||
|
"sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d",
|
||||||
|
"sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d",
|
||||||
|
"sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f",
|
||||||
|
"sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a",
|
||||||
|
"sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b"
|
||||||
|
],
|
||||||
|
"version": "==1.3.1"
|
||||||
|
},
|
||||||
|
"mccabe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||||
|
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||||
|
],
|
||||||
|
"version": "==0.6.1"
|
||||||
|
},
|
||||||
|
"pycodestyle": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:cbc619d09254895b0d12c2c691e237b2e91e9b2ecf5e84c26b35400f93dcfb83",
|
||||||
|
"sha256:cbfca99bd594a10f674d0cd97a3d802a1fdef635d4361e1a2658de47ed261e3a"
|
||||||
|
],
|
||||||
|
"version": "==2.4.0"
|
||||||
|
},
|
||||||
|
"pyflakes": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:9a7662ec724d0120012f6e29d6248ae3727d821bba522a0e6b356eff19126a49",
|
||||||
|
"sha256:f661252913bc1dbe7fcfcbf0af0db3f42ab65aabd1a6ca68fe5d466bace94dae"
|
||||||
|
],
|
||||||
|
"version": "==2.0.0"
|
||||||
|
},
|
||||||
|
"pylint": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:689de29ae747642ab230c6d37be2b969bf75663176658851f456619aacf27492",
|
||||||
|
"sha256:771467c434d0d9f081741fec1d64dfb011ed26e65e12a28fe06ca2f61c4d556c"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.2.2"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||||
|
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||||
|
],
|
||||||
|
"version": "==1.11.0"
|
||||||
|
},
|
||||||
|
"wrapt": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:d4d560d479f2c21e1b5443bbd15fe7ec4b37fe7e53d335d3b9b0a7b1226fe3c6"
|
||||||
|
],
|
||||||
|
"version": "==1.10.11"
|
||||||
|
},
|
||||||
|
"yapf": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8aa7f9abdb97b4da4d3227306b88477982daafef0a96cc41639754ca31f46d55",
|
||||||
|
"sha256:f2df5891481f94ddadfbf8ae8ae499080752cfb06005a31bbb102f3012f8b944"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.25.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
305
app.py
Normal file
305
app.py
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
import sys
|
||||||
|
# from pprint import pprint
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
import csvfile
|
||||||
|
import gui as guimodule
|
||||||
|
import mame
|
||||||
|
import mess
|
||||||
|
from configfile import cfg, config_write, get_emu, get_emulators, set_emu
|
||||||
|
from utils import addscanlines, image_from_data, run_emulator
|
||||||
|
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
GREY = (70, 70, 70)
|
||||||
|
DARKGREY = (50, 50, 50)
|
||||||
|
|
||||||
|
|
||||||
|
class app(object):
|
||||||
|
def __init__(self):
|
||||||
|
if get_emu('EmulatorType') == 'MAME':
|
||||||
|
self.roms = mame.MameROMs(cfg['datadir'], cfg['HideMature'],
|
||||||
|
get_emu())
|
||||||
|
if get_emu('EmulatorType') == 'MESS':
|
||||||
|
self.roms = mess.MessROMs(cfg['datadir'], get_emu())
|
||||||
|
if get_emu('EmulatorType') == 'CSV':
|
||||||
|
self.roms = csvfile.csvROMs(cfg['datadir'], get_emu())
|
||||||
|
|
||||||
|
self.update_menus()
|
||||||
|
|
||||||
|
pygame.init()
|
||||||
|
pygame.font.init()
|
||||||
|
|
||||||
|
iconsurface = pygame.image.load("arcade.png")
|
||||||
|
pygame.display.set_icon(iconsurface)
|
||||||
|
|
||||||
|
if cfg['FullScreen']:
|
||||||
|
self.screen = pygame.display.set_mode(
|
||||||
|
(cfg['ResolutionX'], cfg['ResolutionY']), pygame.FULLSCREEN)
|
||||||
|
else:
|
||||||
|
self.screen = pygame.display.set_mode((cfg['ResolutionX'],
|
||||||
|
cfg['ResolutionY']))
|
||||||
|
|
||||||
|
self.font = pygame.font.Font(
|
||||||
|
pygame.font.match_font(cfg['Font']), cfg['FontSize'])
|
||||||
|
|
||||||
|
self.status_surface = pygame.Surface(( # pylint: disable=E1121
|
||||||
|
cfg['ResolutionX'], self.font.get_height()))
|
||||||
|
|
||||||
|
self.oldmouse = pygame.mouse.set_visible(False)
|
||||||
|
|
||||||
|
pygame.key.set_repeat(100, 30)
|
||||||
|
|
||||||
|
self.listwidth = int(cfg['ResolutionX'] / 2)
|
||||||
|
|
||||||
|
self.gui = guimodule.gui(self.screen)
|
||||||
|
|
||||||
|
self.gui.add_menu(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
self.listwidth,
|
||||||
|
cfg['ResolutionY'] - self.font.get_height(),
|
||||||
|
border=False,
|
||||||
|
data=self.roms,
|
||||||
|
fg=(255, 255, 255),
|
||||||
|
bg=(0, 0, 0),
|
||||||
|
name='MainList',
|
||||||
|
startitem=get_emu('CurrentGame'),
|
||||||
|
itemattop=get_emu('GameAtTop'))
|
||||||
|
|
||||||
|
self.gui.render()
|
||||||
|
|
||||||
|
def update_menus(self):
|
||||||
|
rootmenu = [
|
||||||
|
'Show Artwork', 'Categories', 'Sort',
|
||||||
|
'Hide Clones' if get_emu('ShowClones') else 'Show Clones',
|
||||||
|
'Change Emulator', 'Quit'
|
||||||
|
]
|
||||||
|
sortmenu = ['Name Asc', 'Name Dec', 'Year Asc', 'Year Dec']
|
||||||
|
emulatormenu = get_emulators()
|
||||||
|
self.menus = {
|
||||||
|
'rootmenu': rootmenu,
|
||||||
|
'sortmenu': sortmenu,
|
||||||
|
'emulatormenu': emulatormenu,
|
||||||
|
'categorymenu': self.roms.categories
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
keyup = False
|
||||||
|
quitcount = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
event = self.gui.waitevent()
|
||||||
|
if event:
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
break
|
||||||
|
if event.type == pygame.KEYDOWN:
|
||||||
|
keyup = False
|
||||||
|
if event.key == pygame.K_ESCAPE and self.gui.currentobject == 1: # Main List
|
||||||
|
quitcount += 1
|
||||||
|
if quitcount == cfg['QuitKeyPresses']:
|
||||||
|
break
|
||||||
|
elif event.key == pygame.K_ESCAPE:
|
||||||
|
quitcount = 0
|
||||||
|
if self.gui.currentobject > 1:
|
||||||
|
self.gui.deletelastobject()
|
||||||
|
else:
|
||||||
|
quitcount = 0
|
||||||
|
if event.key in cfg[
|
||||||
|
'KeySelect'] and self.gui.currentobject == 1:
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
print('Running %s' %
|
||||||
|
self.roms[self.gui.getcurrentitem()].name)
|
||||||
|
run_emulator(
|
||||||
|
get_emu('EmulatorType'), get_emu('EXE'),
|
||||||
|
self.roms[self.gui.getcurrentitem()].name)
|
||||||
|
elif event.key in cfg[
|
||||||
|
'KeyGameHistory'] and self.gui.currentobject == 1:
|
||||||
|
history = self.roms.get_history(
|
||||||
|
self.roms[self.gui.getcurrentitem()])
|
||||||
|
if history:
|
||||||
|
self.gui.add_notepad(-1, -1, 800, 600, history,
|
||||||
|
True, WHITE, DARKGREY,
|
||||||
|
'history', 'Game History')
|
||||||
|
elif event.key in cfg[
|
||||||
|
'KeyGameInfo'] and self.gui.currentobject == 1:
|
||||||
|
info = self.roms.get_info(
|
||||||
|
self.roms[self.gui.getcurrentitem()])
|
||||||
|
if info:
|
||||||
|
self.gui.add_notepad(-1, -1, 800, 600, info, True,
|
||||||
|
WHITE, DARKGREY, 'info',
|
||||||
|
'Game Information')
|
||||||
|
elif event.key in cfg[
|
||||||
|
'KeyPopup'] and self.gui.currentobject == 1:
|
||||||
|
self.gui.add_menu(40, 40, -1, -1,
|
||||||
|
self.menus['rootmenu'], True, WHITE,
|
||||||
|
GREY, 'rootmenu', 0)
|
||||||
|
elif event.key in cfg[
|
||||||
|
'KeyPopup'] and self.gui.currentobject > 1:
|
||||||
|
self.gui.deletelastobject()
|
||||||
|
elif event.key in cfg['KeyShowArtwork']:
|
||||||
|
pass
|
||||||
|
# TODO: copy from below
|
||||||
|
elif event.key in cfg['KeySelect']:
|
||||||
|
currentitem = self.gui.getcurrentitem()
|
||||||
|
menuname = self.gui.getmenuname()
|
||||||
|
item = self.menus[menuname][currentitem]
|
||||||
|
if menuname == 'rootmenu':
|
||||||
|
if item == 'Quit':
|
||||||
|
break
|
||||||
|
elif item == 'Show Artwork':
|
||||||
|
print('hello')
|
||||||
|
aw = self.roms.get_artwork(
|
||||||
|
self.roms[self.gui.getcurrentitem(0)])
|
||||||
|
if aw != []:
|
||||||
|
self.gui.add_image_notepad(
|
||||||
|
-1, -1, 800, 600, aw, True, WHITE,
|
||||||
|
DARKGREY, 'artwork', 'Artwork')
|
||||||
|
elif item == 'Categories':
|
||||||
|
self.gui.add_menu(
|
||||||
|
80, 45, -1,
|
||||||
|
-(self.screen.get_height() - 45 -
|
||||||
|
(self.font.get_height() * 2) - 8),
|
||||||
|
self.menus['categorymenu'], True, WHITE,
|
||||||
|
GREY, 'categorymenu', 0)
|
||||||
|
elif item == 'Sort':
|
||||||
|
self.gui.add_menu(80, 45, -1, -1,
|
||||||
|
self.menus['sortmenu'], True,
|
||||||
|
WHITE, GREY, 'sortmenu', 0)
|
||||||
|
elif item == 'Show Clones':
|
||||||
|
set_emu('ShowClones', True)
|
||||||
|
self.update_menus()
|
||||||
|
self.roms.filter()
|
||||||
|
self.gui.deleteallmenus()
|
||||||
|
self.gui.setcurrentitem(0)
|
||||||
|
elif item == 'Hide Clones':
|
||||||
|
set_emu('ShowClones', False)
|
||||||
|
self.update_menus()
|
||||||
|
self.roms.filter()
|
||||||
|
self.gui.deleteallmenus()
|
||||||
|
self.gui.setcurrentitem(0)
|
||||||
|
elif item == 'Change Emulator':
|
||||||
|
self.gui.add_menu(
|
||||||
|
80, 45, -1,
|
||||||
|
-(self.screen.get_height() - 45 -
|
||||||
|
(self.font.get_height() * 2) - 8),
|
||||||
|
self.menus['emulatormenu'], True, WHITE,
|
||||||
|
GREY, 'emulatormenu', 0)
|
||||||
|
elif menuname == 'sortmenu':
|
||||||
|
set_emu('Sort', item)
|
||||||
|
self.roms.filter()
|
||||||
|
self.gui.deleteallmenus()
|
||||||
|
elif menuname == 'categorymenu':
|
||||||
|
set_emu('Category', item)
|
||||||
|
self.roms.filter()
|
||||||
|
self.gui.deleteallmenus()
|
||||||
|
self.gui.setcurrentitem(0)
|
||||||
|
elif menuname == 'emulatormenu':
|
||||||
|
cfg['Emulator'] = item
|
||||||
|
if get_emu('EmulatorType') == 'MAME':
|
||||||
|
self.roms = mame.MameROMs(
|
||||||
|
cfg['datadir'], cfg['HideMature'],
|
||||||
|
get_emu())
|
||||||
|
if get_emu('EmulatorType') == 'MESS':
|
||||||
|
self.roms = mess.MessROMs(
|
||||||
|
cfg['datadir'], get_emu())
|
||||||
|
if get_emu('EmulatorType') == 'CSV':
|
||||||
|
self.roms = csvfile.csvROMs(
|
||||||
|
cfg['datadir'], get_emu())
|
||||||
|
|
||||||
|
self.update_menus()
|
||||||
|
self.gui.deleteallmenus()
|
||||||
|
self.gui.deletelastobject()
|
||||||
|
self.gui.add_menu(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
self.listwidth,
|
||||||
|
cfg['ResolutionY'] - self.font.get_height(),
|
||||||
|
border=False,
|
||||||
|
data=self.roms,
|
||||||
|
fg=(255, 255, 255),
|
||||||
|
bg=(0, 0, 0),
|
||||||
|
name='MainList',
|
||||||
|
startitem=get_emu('CurrentGame'),
|
||||||
|
itemattop=get_emu('GameAtTop'))
|
||||||
|
|
||||||
|
if event.type == pygame.KEYUP:
|
||||||
|
keyup = True
|
||||||
|
|
||||||
|
if self.gui.currentobject == 1 and self.roms:
|
||||||
|
currentitem = self.gui.getcurrentitem()
|
||||||
|
|
||||||
|
self.screen.fill(BLACK)
|
||||||
|
self.status_surface.fill((128, 128, 128))
|
||||||
|
|
||||||
|
if keyup:
|
||||||
|
pass
|
||||||
|
if cfg['AlwaysChangeSnap'] or keyup:
|
||||||
|
data = self.roms.get_snap(self.roms[currentitem], 'snap')
|
||||||
|
screenshot_surface = image_from_data(data, (508, 480))
|
||||||
|
|
||||||
|
if cfg['ScanLines']:
|
||||||
|
screenshot_surface = addscanlines(screenshot_surface)
|
||||||
|
|
||||||
|
self.screen.blit(
|
||||||
|
screenshot_surface,
|
||||||
|
(self.listwidth + 2 + (
|
||||||
|
(self.screen.get_width() - self.listwidth + 2) / 2)
|
||||||
|
- (screenshot_surface.get_width() / 2), 102))
|
||||||
|
|
||||||
|
data = self.roms.get_snap(self.roms[currentitem],
|
||||||
|
'marquee')
|
||||||
|
marquee_surface = image_from_data(data, (640, 100))
|
||||||
|
|
||||||
|
self.screen.blit(
|
||||||
|
marquee_surface,
|
||||||
|
(self.listwidth + 2 + (
|
||||||
|
(self.screen.get_width() - self.listwidth + 2) / 2)
|
||||||
|
- (marquee_surface.get_width() / 2), 0))
|
||||||
|
|
||||||
|
data = self.roms.get_snap(self.roms[currentitem], 'title')
|
||||||
|
title_surface = image_from_data(
|
||||||
|
data, (640, cfg['ResolutionY'] -
|
||||||
|
(screenshot_surface.get_height() +
|
||||||
|
marquee_surface.get_height() + 4 +
|
||||||
|
self.status_surface.get_height())))
|
||||||
|
|
||||||
|
self.screen.blit(
|
||||||
|
title_surface, (self.listwidth + 2 + (
|
||||||
|
(self.screen.get_width() - self.listwidth + 2) / 2)
|
||||||
|
- (title_surface.get_width() / 2),
|
||||||
|
(screenshot_surface.get_height() +
|
||||||
|
marquee_surface.get_height() + 4)))
|
||||||
|
|
||||||
|
size = self.font.size(
|
||||||
|
'%d / %d %s' % (currentitem + 1, len(self.roms),
|
||||||
|
get_emu('Sort')))[0]
|
||||||
|
|
||||||
|
self.status_surface.blit(
|
||||||
|
self.font.render(
|
||||||
|
'%d / %d %s' % (currentitem + 1, len(self.roms),
|
||||||
|
get_emu('Sort')), True, BLACK,
|
||||||
|
(128, 128, 128)), (cfg['ResolutionX'] - size - 5, 0))
|
||||||
|
self.status_surface.blit(
|
||||||
|
self.font.render(
|
||||||
|
'%s %s' % (self.roms[currentitem].year,
|
||||||
|
self.roms[currentitem].category), True,
|
||||||
|
BLACK, (128, 128, 128)), (5, 0))
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.screen.blit(
|
||||||
|
self.status_surface,
|
||||||
|
(0, cfg['ResolutionY'] - self.font.get_height()))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.gui.render()
|
||||||
|
|
||||||
|
pygame.mouse.set_visible(self.oldmouse)
|
||||||
|
pygame.key.set_repeat()
|
||||||
|
|
||||||
|
set_emu('CurrentGame', self.gui.getcurrentitem())
|
||||||
|
set_emu('GameAtTop', self.gui.getitemattop())
|
||||||
|
|
||||||
|
config_write()
|
||||||
BIN
arcade.png
Normal file
BIN
arcade.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
5
cfg/___empty.cfg
Normal file
5
cfg/___empty.cfg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- This file is autogenerated; comments and unknown tags will be stripped -->
|
||||||
|
<mameconfig version="10">
|
||||||
|
<system name="___empty" />
|
||||||
|
</mameconfig>
|
||||||
5
cfg/default.cfg
Normal file
5
cfg/default.cfg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- This file is autogenerated; comments and unknown tags will be stripped -->
|
||||||
|
<mameconfig version="10">
|
||||||
|
<system name="default" />
|
||||||
|
</mameconfig>
|
||||||
137
configfile.py
Normal file
137
configfile.py
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from appdirs import user_data_dir
|
||||||
|
from configobj import ConfigObj, flatten_errors, Section
|
||||||
|
from validate import Validator
|
||||||
|
|
||||||
|
from exclude import MAME_EXCLUDE
|
||||||
|
from utils import get_pygame_keydict, get_keys
|
||||||
|
|
||||||
|
CFG = '''ResolutionX = integer(default=1024)
|
||||||
|
ResolutionY = integer(default=768)
|
||||||
|
FullScreen = boolean(default=False)
|
||||||
|
Font = string(default='microsoftsansserif')
|
||||||
|
FontSize = integer(default=13)
|
||||||
|
QuitKeyPresses = integer(min=1,default=3)
|
||||||
|
ScanLines = boolean(default=True)
|
||||||
|
Key_Up = list(default=list('K_UP'))
|
||||||
|
Key_Down = list(default=list('K_DOWN'))
|
||||||
|
Key_PgUp = list(default=list('K_LEFT','K_PAGEUP'))
|
||||||
|
Key_PgDn = list(default=list('K_RIGHT','K_PAGEDOWN'))
|
||||||
|
Key_Home = list(default=list('K_HOME'))
|
||||||
|
Key_End = list(default=list('K_END'))
|
||||||
|
Key_Select = list(default=list('K_RETURN','K_1'))
|
||||||
|
Key_GameInfo = list(default=list('K_5'))
|
||||||
|
Key_GameHistory = list(default=list('K_6'))
|
||||||
|
Key_Popup = list(default=list('K_2'))
|
||||||
|
Key_ShowArtwork = list(default=list())
|
||||||
|
AlwaysChangeSnap = boolean(default=True)
|
||||||
|
HideMature = boolean(default=True)
|
||||||
|
Emulator = string()
|
||||||
|
[__many__]
|
||||||
|
EXE = string()
|
||||||
|
Version = string(default=None)
|
||||||
|
ShowClones = boolean(default=True)
|
||||||
|
Category = string(default='All Games')
|
||||||
|
GameAtTop = integer(min=0,default=0)
|
||||||
|
CurrentGame = integer(min=0,default=0)
|
||||||
|
EmulatorType = option('MAME','MESS','CSV',default='MAME')
|
||||||
|
StatusFilter = list(default=list())
|
||||||
|
Sort = option('Name Asc','Name Dec','Year Asc','Year Dec',default='Name Asc')
|
||||||
|
SnapDir = string(default=None)
|
||||||
|
CSVFile = string(default=None)
|
||||||
|
ArtworkDirs = list(default=list())
|
||||||
|
'''
|
||||||
|
|
||||||
|
appname = 'MFE'
|
||||||
|
appauthor = 'RMJ'
|
||||||
|
|
||||||
|
datadir = user_data_dir(appname, appauthor)
|
||||||
|
datadir = os.getcwd()
|
||||||
|
|
||||||
|
if not os.path.isdir(datadir):
|
||||||
|
os.makedirs(datadir)
|
||||||
|
|
||||||
|
spec = CFG.split('\n')
|
||||||
|
cfg = ConfigObj(
|
||||||
|
os.path.join(datadir, 'mfe.ini'), configspec=spec, encoding='UTF8')
|
||||||
|
|
||||||
|
validator = Validator()
|
||||||
|
result = cfg.validate(validator, copy=True, preserve_errors=True)
|
||||||
|
|
||||||
|
stop = False
|
||||||
|
for entry in flatten_errors(cfg, result):
|
||||||
|
section_list, key, error = entry
|
||||||
|
if key is not None:
|
||||||
|
section_list.append(key)
|
||||||
|
else:
|
||||||
|
section_list.append('[missing section]')
|
||||||
|
section_string = ', '.join(section_list)
|
||||||
|
if error is False:
|
||||||
|
error = 'Missing value or section.'
|
||||||
|
print(section_string, ' = ', error)
|
||||||
|
stop = True
|
||||||
|
|
||||||
|
cfg.write()
|
||||||
|
|
||||||
|
if stop:
|
||||||
|
exit()
|
||||||
|
|
||||||
|
cfg["datadir"] = datadir
|
||||||
|
|
||||||
|
if not os.path.isfile(os.path.join(datadir, 'mame_exclude.txt')):
|
||||||
|
with open(os.path.join(datadir, "mame_exclude.txt"), "wt") as f:
|
||||||
|
f.write(MAME_EXCLUDE)
|
||||||
|
|
||||||
|
keys = get_pygame_keydict()
|
||||||
|
cfg['KeyUp'] = get_keys(keys, cfg['Key_Up'])
|
||||||
|
cfg['KeyDown'] = get_keys(keys, cfg['Key_Down'])
|
||||||
|
cfg['KeyPgUp'] = get_keys(keys, cfg['Key_PgUp'])
|
||||||
|
cfg['KeyPgDn'] = get_keys(keys, cfg['Key_PgDn'])
|
||||||
|
cfg['KeyHome'] = get_keys(keys, cfg['Key_Home'])
|
||||||
|
cfg['KeyEnd'] = get_keys(keys, cfg['Key_End'])
|
||||||
|
cfg['KeySelect'] = get_keys(keys, cfg['Key_Select'])
|
||||||
|
cfg['KeyGameInfo'] = get_keys(keys, cfg['Key_GameInfo'])
|
||||||
|
cfg['KeyGameHistory'] = get_keys(keys, cfg['Key_GameHistory'])
|
||||||
|
cfg['KeyPopup'] = get_keys(keys, cfg['Key_Popup'])
|
||||||
|
cfg['KeyShowArtwork'] = get_keys(keys, cfg['Key_ShowArtwork'])
|
||||||
|
|
||||||
|
|
||||||
|
def config_write():
|
||||||
|
Exclude = [
|
||||||
|
'KeyUp', 'KeyDown', 'KeyPgUp', 'KeyPgDn', 'KeySelect', 'KeyGameInfo',
|
||||||
|
'KeyGameHistory', 'KeyPopup', 'KeyHome', 'KeyEnd', 'KeyShowArtwork',
|
||||||
|
'datadir'
|
||||||
|
]
|
||||||
|
|
||||||
|
t = {}
|
||||||
|
for item in Exclude:
|
||||||
|
t[item] = cfg[item]
|
||||||
|
del cfg[item]
|
||||||
|
|
||||||
|
cfg.write()
|
||||||
|
|
||||||
|
for item in t:
|
||||||
|
cfg[item] = t[item]
|
||||||
|
|
||||||
|
|
||||||
|
def get_emu(option=None):
|
||||||
|
if option:
|
||||||
|
r = cfg[cfg['Emulator']][option]
|
||||||
|
else:
|
||||||
|
r = cfg[cfg['Emulator']]
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def set_emu(option, data):
|
||||||
|
cfg[cfg['Emulator']][option] = data
|
||||||
|
|
||||||
|
|
||||||
|
def get_emulators():
|
||||||
|
r = []
|
||||||
|
for k in cfg.keys():
|
||||||
|
if isinstance(cfg[k], Section):
|
||||||
|
r.append(k)
|
||||||
|
return r
|
||||||
143
csvfile.py
Normal file
143
csvfile.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
|
||||||
|
class csvROM(object):
|
||||||
|
''' One CSV ROM '''
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.name = ''
|
||||||
|
self.cloneof = None
|
||||||
|
self.description = ''
|
||||||
|
self.year = ''
|
||||||
|
self.manufacturer = ''
|
||||||
|
self.status = ''
|
||||||
|
self.category = ''
|
||||||
|
self.snap = None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.description
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s("%s", "%s", "%s", "%s", "%s", "%s", "%s")' % (
|
||||||
|
self.__class__.__name__, self.name, self.cloneof, self.description,
|
||||||
|
self.year, self.manufacturer, self.status, self.category)
|
||||||
|
|
||||||
|
|
||||||
|
class csvROMs(object):
|
||||||
|
''' A Collection of CSV Roms '''
|
||||||
|
|
||||||
|
def __init__(self, data_dir, cfg):
|
||||||
|
self.all_roms = []
|
||||||
|
self.roms = []
|
||||||
|
self.all_categories = []
|
||||||
|
self.categories = []
|
||||||
|
self.catdict = {}
|
||||||
|
self.len = 0
|
||||||
|
self.data_dir = data_dir
|
||||||
|
self.cfg = cfg
|
||||||
|
self.emulator_dir = os.path.split(cfg['EXE'])[0]
|
||||||
|
|
||||||
|
self.parse()
|
||||||
|
self.filter()
|
||||||
|
|
||||||
|
self.snapdir = cfg['SnapDir']
|
||||||
|
if os.path.isfile(os.path.join(self.snapdir, 'snap.zip')):
|
||||||
|
self.snapfile = zipfile.ZipFile(
|
||||||
|
os.path.join(self.snapdir, "snap.zip"))
|
||||||
|
else:
|
||||||
|
self.snapfile = None
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.len
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.roms[item]
|
||||||
|
|
||||||
|
def filter(self):
|
||||||
|
self.roms = []
|
||||||
|
|
||||||
|
for rom in self.all_roms:
|
||||||
|
if self.cfg["Category"] == "All Games" or self.cfg["Category"] == rom.category:
|
||||||
|
self.roms.append(rom)
|
||||||
|
|
||||||
|
if self.cfg["Sort"] == "Name Asc":
|
||||||
|
self.roms.sort(key=lambda x: x.description.lower(), reverse=False)
|
||||||
|
elif self.cfg["Sort"] == "Name Dec":
|
||||||
|
self.roms.sort(key=lambda x: x.description.lower(), reverse=True)
|
||||||
|
elif self.cfg["Sort"] == "Year Asc":
|
||||||
|
self.roms.sort(key=lambda x: x.year.lower(), reverse=False)
|
||||||
|
elif self.cfg["Sort"] == "Year Dec":
|
||||||
|
self.roms.sort(key=lambda x: x.year.lower(), reverse=True)
|
||||||
|
|
||||||
|
self.len = len(self.roms)
|
||||||
|
|
||||||
|
self.categories = self.all_categories
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
''' Parse csv file '''
|
||||||
|
if os.path.isfile(self.cfg['CSVFile']):
|
||||||
|
with open(self.cfg['CSVFile']) as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
|
||||||
|
for game in reader:
|
||||||
|
rom = csvROM()
|
||||||
|
rom.name = game['romname']
|
||||||
|
rom.description = game[
|
||||||
|
'description'] if 'description' in game.keys(
|
||||||
|
) else game['romname']
|
||||||
|
rom.snap = game['snap'] if 'snap' in game.keys() else game[
|
||||||
|
'romname']
|
||||||
|
rom.year = game[
|
||||||
|
'year'] if 'year' in game.keys() else 'Unknown Year'
|
||||||
|
rom.category = game[
|
||||||
|
'category'] if 'category' in game.keys() else 'Unknown'
|
||||||
|
self.all_roms.append(rom)
|
||||||
|
|
||||||
|
self.all_categories.insert(0, 'All Games')
|
||||||
|
self.all_categories.append('Unknown')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_snap(self, rom, type_of_image): # pylint : disable=R0912
|
||||||
|
data = None
|
||||||
|
image_file = None
|
||||||
|
image_dir = None
|
||||||
|
|
||||||
|
if type_of_image == 'snap':
|
||||||
|
image_dir = self.snapdir
|
||||||
|
image_file = self.snapfile
|
||||||
|
|
||||||
|
if image_file:
|
||||||
|
try:
|
||||||
|
data = image_file.read(rom.name + ".png")
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
data = image_file.read(rom.cloneof + '.png')
|
||||||
|
except:
|
||||||
|
data = None
|
||||||
|
elif image_dir:
|
||||||
|
fn = os.path.join(image_dir, rom.name + '.png')
|
||||||
|
if os.path.isfile(fn):
|
||||||
|
with open(fn, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
elif rom.cloneof:
|
||||||
|
fn = os.path.join(image_dir, rom.cloneof + '.png')
|
||||||
|
if os.path.isfile(fn):
|
||||||
|
with open(fn, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_info(self, rom):
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_history(self, rom):
|
||||||
|
return
|
||||||
99
exclude.py
Normal file
99
exclude.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
MAME_EXCLUDE = """3D Printer
|
||||||
|
Astrological Computer
|
||||||
|
Audio Sequencer
|
||||||
|
Bank-teller Terminal
|
||||||
|
Barcode Printer
|
||||||
|
Bridge Machine
|
||||||
|
Business Computer / Terminal
|
||||||
|
Calculator / Pocket Computer
|
||||||
|
Cash Counter
|
||||||
|
Chess Machine
|
||||||
|
Clock
|
||||||
|
Credit Card Terminal
|
||||||
|
DVD Player
|
||||||
|
DVD Reader/Writer
|
||||||
|
Dame Machine
|
||||||
|
Development Computer
|
||||||
|
Devices
|
||||||
|
Document Processors
|
||||||
|
Dot-Matrix Display
|
||||||
|
Drum Machine
|
||||||
|
EPROM Programmer
|
||||||
|
Educational Game
|
||||||
|
Electromechanical / Change Money
|
||||||
|
Electromechanical / Coin Pusher
|
||||||
|
Electromechanical / Misc.
|
||||||
|
Electromechanical / Pinball
|
||||||
|
Electromechanical / Redemption
|
||||||
|
Electromechanical / Reels
|
||||||
|
Electromechanical / Utilities
|
||||||
|
Electronic Board Game
|
||||||
|
Electronic Typewriter
|
||||||
|
Engine Control Unit
|
||||||
|
Gambling Board
|
||||||
|
Game Console
|
||||||
|
Game Console Expansion
|
||||||
|
Graphic Tablet
|
||||||
|
Graphics Display Controller
|
||||||
|
Handheld Child Computers
|
||||||
|
Handheld Game
|
||||||
|
Handheld Game Console
|
||||||
|
Home Computer
|
||||||
|
In Circuit Emulator
|
||||||
|
JukeBox
|
||||||
|
Kit Computer
|
||||||
|
Laptop / Notebook / Portable
|
||||||
|
Laser Printer
|
||||||
|
Matrix Printer
|
||||||
|
Microcomputer
|
||||||
|
Misc.
|
||||||
|
Misc. * Mature *
|
||||||
|
Mobile Phone
|
||||||
|
Modem
|
||||||
|
Multi-cart Board
|
||||||
|
Network Processor
|
||||||
|
Not Classified
|
||||||
|
Pinball
|
||||||
|
Pinball * Mature *
|
||||||
|
Pinball / Pachinko
|
||||||
|
Pinball / Pachinko * Mature *
|
||||||
|
Player
|
||||||
|
Pocket Device / Pad / PDA
|
||||||
|
Portable Media Player
|
||||||
|
Print Club
|
||||||
|
Printer Handbook
|
||||||
|
Programming Machine
|
||||||
|
Punched Card Computer
|
||||||
|
Quiz / Chinese
|
||||||
|
Quiz / French
|
||||||
|
Quiz / German
|
||||||
|
Quiz / Italian
|
||||||
|
Quiz / Japanese
|
||||||
|
Quiz / Japanese * Mature *
|
||||||
|
Quiz / Japanese - Music
|
||||||
|
Quiz / Korean
|
||||||
|
Quiz / Spanish
|
||||||
|
Rhythm / Dance
|
||||||
|
Rhythm / Instruments
|
||||||
|
Rhythm / Misc.
|
||||||
|
Robot Control
|
||||||
|
Satellite Receiver
|
||||||
|
Single Board Computer
|
||||||
|
Speech Synthesizer
|
||||||
|
Synthesizer
|
||||||
|
System / BIOS
|
||||||
|
System / Device
|
||||||
|
Talking Calculator
|
||||||
|
Telephone / ComputerPhone
|
||||||
|
Test ROM
|
||||||
|
Thermal Printer
|
||||||
|
Toy cars
|
||||||
|
Training Board
|
||||||
|
Utilities / Test
|
||||||
|
Utilities / Update
|
||||||
|
VTR Control
|
||||||
|
Virtual Environment
|
||||||
|
Wavetables Generator
|
||||||
|
Word-processing Machine
|
||||||
|
Workstation / Server
|
||||||
|
"""
|
||||||
413
gui.py
Normal file
413
gui.py
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
import pygame
|
||||||
|
# pylint: disable=E0611
|
||||||
|
from pygame.locals import KEYDOWN
|
||||||
|
# pylint: enable=E0611
|
||||||
|
|
||||||
|
from configfile import cfg
|
||||||
|
from utils import wrap_multi_line, image_from_data
|
||||||
|
|
||||||
|
# pylint: disable=E1121,R0902,R0903,R0912,R0913
|
||||||
|
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class baseobject(object):
|
||||||
|
def __init__(self,
|
||||||
|
surface,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
font,
|
||||||
|
border=False,
|
||||||
|
fg=WHITE,
|
||||||
|
bg=BLACK,
|
||||||
|
name=None,
|
||||||
|
title=None):
|
||||||
|
self.surface = surface
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.font = font
|
||||||
|
self.border = border
|
||||||
|
self.fontheight = self.font.get_height()
|
||||||
|
self.fg = fg
|
||||||
|
self.bg = bg
|
||||||
|
self.data = data
|
||||||
|
self.name = name
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
if self.width < 1:
|
||||||
|
for item in self.data:
|
||||||
|
size = self.font.size(item)[0]
|
||||||
|
if size > self.width:
|
||||||
|
self.width = size
|
||||||
|
if self.width > self.surface.get_width():
|
||||||
|
self.surface.get_width()
|
||||||
|
if self.border:
|
||||||
|
self.width += 8
|
||||||
|
|
||||||
|
if self.height < 1:
|
||||||
|
self.height = self.fontheight * (len(self.data))
|
||||||
|
if self.border:
|
||||||
|
self.height += 8
|
||||||
|
if self.height > self.surface.get_height():
|
||||||
|
if self.border:
|
||||||
|
self.height = self.surface.get_height() - 8 - self.y
|
||||||
|
else:
|
||||||
|
self.height = self.surface.get_height - self.y
|
||||||
|
|
||||||
|
if self.x == -1:
|
||||||
|
self.x = int((self.surface.get_width() / 2) - (self.width / 2))
|
||||||
|
|
||||||
|
if self.y == -1:
|
||||||
|
self.y = int((self.surface.get_height() / 2) - (self.height / 2))
|
||||||
|
|
||||||
|
if self.border:
|
||||||
|
if not self.title:
|
||||||
|
self.textx = self.x + 4
|
||||||
|
self.texty = self.y + 4
|
||||||
|
self.textwidth = self.width - 8
|
||||||
|
self.textheight = self.height - 8
|
||||||
|
else:
|
||||||
|
self.textx = self.x + 4
|
||||||
|
self.texty = self.y + 4 + self.fontheight
|
||||||
|
self.textwidth = self.width - 8
|
||||||
|
self.textheight = self.height - 8 - self.fontheight
|
||||||
|
else:
|
||||||
|
if not self.title:
|
||||||
|
self.textx = self.x
|
||||||
|
self.texty = self.y
|
||||||
|
self.textwidth = self.width
|
||||||
|
self.textheight = self.height
|
||||||
|
else:
|
||||||
|
self.textx = self.x
|
||||||
|
self.texty = self.y + self.fontheight
|
||||||
|
self.textwidth = self.width
|
||||||
|
self.textheight = self.height - self.fontheight
|
||||||
|
|
||||||
|
|
||||||
|
class menu(baseobject):
|
||||||
|
def __init__(self,
|
||||||
|
surface,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
font,
|
||||||
|
border=False,
|
||||||
|
fg=WHITE,
|
||||||
|
bg=BLACK,
|
||||||
|
name=None,
|
||||||
|
startitem=0,
|
||||||
|
itemattop=0):
|
||||||
|
baseobject.__init__(self, surface, x, y, width, height, data, font,
|
||||||
|
border, fg, bg, name)
|
||||||
|
self.currentitem = startitem
|
||||||
|
self.itemattop = itemattop
|
||||||
|
self.itemsperpage = int(self.textheight / self.fontheight)
|
||||||
|
self.isrom = True
|
||||||
|
if str(type(self.data[0])) == "<class 'str'>":
|
||||||
|
self.isrom = False
|
||||||
|
|
||||||
|
def processevent(self, event): # pylint: disable=R0912
|
||||||
|
if event.type == KEYDOWN:
|
||||||
|
if event.key in cfg["KeyUp"] and self.currentitem > 0:
|
||||||
|
if self.currentitem == self.itemattop:
|
||||||
|
self.itemattop -= 1
|
||||||
|
self.currentitem -= 1
|
||||||
|
if event.key in cfg["KeyDown"] and self.currentitem < len(
|
||||||
|
self.data) - 1:
|
||||||
|
self.currentitem += 1
|
||||||
|
if self.itemattop + self.itemsperpage <= self.currentitem:
|
||||||
|
self.itemattop += 1
|
||||||
|
if event.key in cfg["KeyPgUp"] and self.currentitem > 0:
|
||||||
|
self.currentitem -= self.itemsperpage - 1
|
||||||
|
while self.itemattop > self.currentitem:
|
||||||
|
self.itemattop -= 1
|
||||||
|
if self.itemattop < 0:
|
||||||
|
self.itemattop = 0
|
||||||
|
self.currentitem = 0
|
||||||
|
if event.key in cfg["KeyPgDn"] and self.currentitem < len(
|
||||||
|
self.data) - 1:
|
||||||
|
self.currentitem += self.itemsperpage - 1
|
||||||
|
while self.currentitem - self.itemattop >= self.itemsperpage:
|
||||||
|
self.itemattop += 1
|
||||||
|
if self.itemattop + self.itemsperpage > len(self.data):
|
||||||
|
self.itemattop = len(self.data) - self.itemsperpage
|
||||||
|
self.currentitem = len(self.data) - 1
|
||||||
|
if event.key in cfg["KeyHome"] and self.currentitem > 0:
|
||||||
|
self.currentitem = 0
|
||||||
|
self.itemattop = 0
|
||||||
|
if event.key in cfg["KeyEnd"] and self.currentitem < len(
|
||||||
|
self.data) - 1:
|
||||||
|
self.currentitem = len(self.data) - 1
|
||||||
|
self.itemattop = self.currentitem - self.itemsperpage + 1
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
pygame.Surface.fill(
|
||||||
|
self.surface,
|
||||||
|
self.bg,
|
||||||
|
rect=pygame.Rect(self.x, self.y, self.width, self.height))
|
||||||
|
|
||||||
|
if self.border:
|
||||||
|
pygame.draw.rect(self.surface, self.fg,
|
||||||
|
pygame.Rect(self.x + 1, self.y + 1,
|
||||||
|
self.width - 2, self.height - 2), 1)
|
||||||
|
|
||||||
|
for i in range(0, self.itemsperpage):
|
||||||
|
if i > len(self.data) - 1:
|
||||||
|
t = pygame.Surface((self.textwidth, self.fontheight))
|
||||||
|
t.fill(self.bg)
|
||||||
|
self.surface.blit(t, (self.textx,
|
||||||
|
self.texty + i * self.fontheight))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.isrom:
|
||||||
|
item = self.data[self.itemattop + i].description
|
||||||
|
else:
|
||||||
|
item = self.data[self.itemattop + i]
|
||||||
|
|
||||||
|
if self.itemattop + i == self.currentitem:
|
||||||
|
s = self.font.render(item, True, self.bg, self.fg)
|
||||||
|
t = pygame.Surface((self.textwidth, self.fontheight))
|
||||||
|
t.fill(self.fg)
|
||||||
|
t.blit(s, (0, 0))
|
||||||
|
self.surface.blit(t, (self.textx,
|
||||||
|
self.texty + i * self.fontheight))
|
||||||
|
else:
|
||||||
|
s = self.font.render(item, True, self.fg, self.bg)
|
||||||
|
t = pygame.Surface((self.textwidth, self.fontheight))
|
||||||
|
t.fill(self.bg)
|
||||||
|
t.blit(s, (0, 0))
|
||||||
|
self.surface.blit(t, (self.textx,
|
||||||
|
self.texty + i * self.fontheight))
|
||||||
|
|
||||||
|
|
||||||
|
class notepad(baseobject):
|
||||||
|
def __init__(self,
|
||||||
|
surface,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
font,
|
||||||
|
border=False,
|
||||||
|
fg=WHITE,
|
||||||
|
bg=BLACK,
|
||||||
|
name="None",
|
||||||
|
title=None):
|
||||||
|
baseobject.__init__(self, surface, x, y, width, height, data, font,
|
||||||
|
border, fg, bg, name, title)
|
||||||
|
self.data = wrap_multi_line(data, self.font, self.textwidth)
|
||||||
|
self.lineattop = 0
|
||||||
|
self.linesperpage = int(self.textheight / self.fontheight)
|
||||||
|
self.lasttopline = int(len(self.data) - self.linesperpage)
|
||||||
|
|
||||||
|
def processevent(self, event):
|
||||||
|
if event.type == KEYDOWN:
|
||||||
|
if event.key in cfg["KeyDown"] and self.lineattop < self.lasttopline:
|
||||||
|
self.lineattop += 1
|
||||||
|
elif event.key in cfg["KeyUp"] and self.lineattop > 0:
|
||||||
|
self.lineattop -= 1
|
||||||
|
elif event.key in cfg["KeyPgDn"] and self.lineattop < self.lasttopline:
|
||||||
|
self.lineattop += self.linesperpage
|
||||||
|
if self.lineattop > self.lasttopline:
|
||||||
|
self.lineattop = self.lasttopline
|
||||||
|
elif event.key in cfg["KeyPgUp"] and self.lineattop > 0:
|
||||||
|
self.lineattop -= self.linesperpage
|
||||||
|
if self.lineattop < 0:
|
||||||
|
self.lineattop = 0
|
||||||
|
elif event.key in cfg["KeyHome"] and self.lineattop > 0:
|
||||||
|
self.lineattop = 0
|
||||||
|
elif event.key in cfg["KeyEnd"] and self.lineattop < self.lasttopline:
|
||||||
|
self.lineattop = self.lasttopline
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
pygame.Surface.fill(
|
||||||
|
self.surface,
|
||||||
|
self.bg,
|
||||||
|
rect=pygame.Rect(self.x, self.y, self.width, self.height))
|
||||||
|
if self.border:
|
||||||
|
pygame.draw.rect(self.surface, self.fg,
|
||||||
|
pygame.Rect(self.x + 1, self.y + 1,
|
||||||
|
self.width - 2, self.height - 2), 1)
|
||||||
|
if self.title:
|
||||||
|
x = (self.textwidth / 2) - (
|
||||||
|
self.font.size('%s' % self.title)[0] / 2)
|
||||||
|
title_surface = pygame.Surface((self.textwidth,
|
||||||
|
self.fontheight))
|
||||||
|
title_surface.fill(self.fg)
|
||||||
|
title_surface.blit(
|
||||||
|
self.font.render('%s' % self.title, True, BLACK, self.fg),
|
||||||
|
(x, 0))
|
||||||
|
self.surface.blit(title_surface,
|
||||||
|
(self.textx, self.texty - self.fontheight))
|
||||||
|
for c in range(0, self.linesperpage):
|
||||||
|
try:
|
||||||
|
itemsurface = self.font.render(self.data[c + self.lineattop],
|
||||||
|
True, self.fg, self.bg)
|
||||||
|
self.surface.blit(itemsurface,
|
||||||
|
(self.textx,
|
||||||
|
self.texty + c * self.fontheight))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class image_notepad(baseobject):
|
||||||
|
def __init__(self,
|
||||||
|
surface,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
font,
|
||||||
|
border=False,
|
||||||
|
fg=WHITE,
|
||||||
|
bg=BLACK,
|
||||||
|
name="None",
|
||||||
|
title=None):
|
||||||
|
baseobject.__init__(self, surface, x, y, width, height, data, font,
|
||||||
|
border, fg, bg, name, title)
|
||||||
|
self.currentitem = 0
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def processevent(self, event):
|
||||||
|
if event.type == KEYDOWN:
|
||||||
|
if event.key in cfg["KeyPgDn"]:
|
||||||
|
if self.currentitem < len(self.data) - 1:
|
||||||
|
self.currentitem += 1
|
||||||
|
elif event.key in cfg["KeyPgUp"]:
|
||||||
|
if self.currentitem > 0:
|
||||||
|
self.currentitem -= 1
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
pygame.Surface.fill(
|
||||||
|
self.surface,
|
||||||
|
self.bg,
|
||||||
|
rect=pygame.Rect(self.x, self.y, self.width, self.height))
|
||||||
|
if self.border:
|
||||||
|
pygame.draw.rect(self.surface, self.fg,
|
||||||
|
pygame.Rect(self.x + 1, self.y + 1,
|
||||||
|
self.width - 2, self.height - 2), 1)
|
||||||
|
x = (self.textwidth / 2) - (self.font.size('%s' % self.title)[0] / 2)
|
||||||
|
title_surface = pygame.Surface((self.textwidth, self.fontheight))
|
||||||
|
title_surface.fill(self.fg)
|
||||||
|
title_surface.blit(
|
||||||
|
self.font.render('%s' % self.data[self.currentitem][0], True,
|
||||||
|
BLACK, self.fg), (x, 0))
|
||||||
|
self.surface.blit(title_surface, (self.textx,
|
||||||
|
self.texty - self.fontheight))
|
||||||
|
|
||||||
|
image_surface = image_from_data(self.data[self.currentitem][1],
|
||||||
|
(self.textwidth, self.textheight))
|
||||||
|
|
||||||
|
w, h = image_surface.get_width(), image_surface.get_height()
|
||||||
|
|
||||||
|
x = (self.textwidth / 2) - (w / 2) + self.textx
|
||||||
|
y = (self.textheight / 2) - (h / 2) + self.texty
|
||||||
|
|
||||||
|
self.surface.blit(image_surface, (x, y))
|
||||||
|
|
||||||
|
|
||||||
|
class gui():
|
||||||
|
def __init__(self, surface):
|
||||||
|
self.surface = surface
|
||||||
|
self.objects = []
|
||||||
|
self.font = pygame.font.Font(
|
||||||
|
pygame.font.match_font(cfg["Font"], bold=True), cfg["FontSize"])
|
||||||
|
# self.fontheight = self.font.get_height()
|
||||||
|
self.currentobject = 0
|
||||||
|
|
||||||
|
def waitevent(self):
|
||||||
|
event = pygame.event.wait()
|
||||||
|
|
||||||
|
if self.objects:
|
||||||
|
self.objects[-1].processevent(event)
|
||||||
|
|
||||||
|
return event
|
||||||
|
|
||||||
|
def add_menu(self,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
border=False,
|
||||||
|
fg=WHITE,
|
||||||
|
bg=BLACK,
|
||||||
|
name="None",
|
||||||
|
startitem=0,
|
||||||
|
itemattop=0):
|
||||||
|
self.currentobject += 1
|
||||||
|
self.objects.append(
|
||||||
|
menu(self.surface, x, y, width, height, data, self.font, border,
|
||||||
|
fg, bg, name, startitem, itemattop))
|
||||||
|
|
||||||
|
def add_notepad(self,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
border=False,
|
||||||
|
fg=WHITE,
|
||||||
|
bg=BLACK,
|
||||||
|
name="None",
|
||||||
|
title=None):
|
||||||
|
self.currentobject += 1
|
||||||
|
self.objects.append(
|
||||||
|
notepad(self.surface, x, y, width, height, data, self.font, border,
|
||||||
|
fg, bg, name, title))
|
||||||
|
|
||||||
|
def add_image_notepad(self,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
border=False,
|
||||||
|
fg=WHITE,
|
||||||
|
bg=BLACK,
|
||||||
|
name="None",
|
||||||
|
title=None):
|
||||||
|
self.currentobject += 1
|
||||||
|
self.objects.append(
|
||||||
|
image_notepad(self.surface, x, y, width, height, data, self.font,
|
||||||
|
border, fg, bg, name, title))
|
||||||
|
|
||||||
|
def deletelastobject(self):
|
||||||
|
del self.objects[-1]
|
||||||
|
self.currentobject -= 1
|
||||||
|
|
||||||
|
def getmenuname(self):
|
||||||
|
return self.objects[-1].name
|
||||||
|
|
||||||
|
def deleteallmenus(self):
|
||||||
|
while self.currentobject > 1:
|
||||||
|
self.deletelastobject()
|
||||||
|
|
||||||
|
def getcurrentitem(self, objnum=None):
|
||||||
|
if objnum is None:
|
||||||
|
return self.objects[-1].currentitem
|
||||||
|
return self.objects[objnum].currentitem
|
||||||
|
|
||||||
|
def getitemattop(self):
|
||||||
|
return self.objects[-1].itemattop
|
||||||
|
|
||||||
|
# TODO: Fix me for itemattop on normal menus
|
||||||
|
def setcurrentitem(self, selected_item):
|
||||||
|
self.objects[-1].itemattop = selected_item
|
||||||
|
self.objects[-1].currentitem = selected_item
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
for t in self.objects:
|
||||||
|
t.render()
|
||||||
|
|
||||||
|
pygame.display.update()
|
||||||
BIN
images/arcade.icns
Normal file
BIN
images/arcade.icns
Normal file
Binary file not shown.
BIN
images/arcade.ico
Normal file
BIN
images/arcade.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
406
mame.py
Normal file
406
mame.py
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import zipfile
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
from titlecase import titlecase
|
||||||
|
|
||||||
|
|
||||||
|
class MameROM(object):
|
||||||
|
''' One Mame ROM '''
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.name = ''
|
||||||
|
self.cloneof = None
|
||||||
|
self.description = ''
|
||||||
|
self.year = ''
|
||||||
|
self.manufacturer = ''
|
||||||
|
self.status = ''
|
||||||
|
self.category = ''
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.description
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s("%s", "%s", "%s", "%s", "%s", "%s", "%s")' % (
|
||||||
|
self.__class__.__name__, self.name, self.cloneof, self.description,
|
||||||
|
self.year, self.manufacturer, self.status, self.category)
|
||||||
|
|
||||||
|
|
||||||
|
class MameROMs(object):
|
||||||
|
''' A Collection of MAME Roms '''
|
||||||
|
|
||||||
|
def __init__(self, data_dir, hide_mature, cfg):
|
||||||
|
self.all_roms = []
|
||||||
|
self.roms = []
|
||||||
|
self.all_categories = []
|
||||||
|
self.categories = []
|
||||||
|
self.catdict = {}
|
||||||
|
self.len = 0
|
||||||
|
self.data_dir = data_dir
|
||||||
|
self.hide_mature = hide_mature
|
||||||
|
self.cfg = cfg
|
||||||
|
self.emulator_dir = os.path.split(cfg['EXE'])[0]
|
||||||
|
|
||||||
|
self.parse()
|
||||||
|
self.filter()
|
||||||
|
|
||||||
|
self.snapdir = os.path.join(self.emulator_dir, 'snap')
|
||||||
|
if os.path.isfile(os.path.join(self.snapdir, 'snap.zip')):
|
||||||
|
self.snapfile = zipfile.ZipFile(
|
||||||
|
os.path.join(self.snapdir, "snap.zip"))
|
||||||
|
else:
|
||||||
|
self.snapfile = None
|
||||||
|
self.snapdir = os.path.join(self.snapdir, 'snap')
|
||||||
|
|
||||||
|
self.marqueesdir = os.path.join(self.emulator_dir, 'marquees')
|
||||||
|
if os.path.isfile(os.path.join(self.marqueesdir, 'marquees.zip')):
|
||||||
|
self.marqueesfile = zipfile.ZipFile(
|
||||||
|
os.path.join(self.marqueesdir, "marquees.zip"))
|
||||||
|
else:
|
||||||
|
self.marqueesfile = None
|
||||||
|
self.marqueesdir = os.path.join(self.marqueesdir, 'marquees')
|
||||||
|
|
||||||
|
self.titlesdir = os.path.join(self.emulator_dir, 'titles')
|
||||||
|
if os.path.isfile(os.path.join(self.titlesdir, 'titles.zip')):
|
||||||
|
self.titlesfile = zipfile.ZipFile(
|
||||||
|
os.path.join(self.titlesdir, "titles.zip"))
|
||||||
|
else:
|
||||||
|
self.titlesfile = None
|
||||||
|
self.titlesdir = os.path.join(self.titlesdir, 'titles')
|
||||||
|
|
||||||
|
infofn = os.path.join(self.emulator_dir, 'mameinfo.dat')
|
||||||
|
if not os.path.isfile(infofn):
|
||||||
|
self.info = None
|
||||||
|
else:
|
||||||
|
with open(infofn, 'rt', encoding='latin1') as f:
|
||||||
|
self.info = f.read()
|
||||||
|
|
||||||
|
historyfn = os.path.join(self.emulator_dir, 'history.dat')
|
||||||
|
if not os.path.isfile(historyfn):
|
||||||
|
self.history = None
|
||||||
|
else:
|
||||||
|
with open(historyfn, 'rt', encoding='utf-8') as f:
|
||||||
|
self.history = f.read()
|
||||||
|
|
||||||
|
self.artwork_dirs = {}
|
||||||
|
for item in cfg['ArtworkDirs']:
|
||||||
|
directory = os.path.join(self.emulator_dir, item)
|
||||||
|
if os.path.isdir(directory):
|
||||||
|
if os.path.isfile(os.path.join(directory, '%s.zip' % item)):
|
||||||
|
self.artwork_dirs[titlecase(item)] = {
|
||||||
|
"dof": os.path.join(directory, '%s.zip' % item),
|
||||||
|
'type': 'file'
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
self.artwork_dirs[titlecase(item)] = {
|
||||||
|
"dof": directory,
|
||||||
|
'type': 'dir'
|
||||||
|
}
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.len
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.roms[item]
|
||||||
|
|
||||||
|
def filter(self):
|
||||||
|
self.roms = []
|
||||||
|
|
||||||
|
for rom in self.all_roms:
|
||||||
|
if rom.status in self.cfg['StatusFilter']:
|
||||||
|
if self.cfg["Category"] == "All Games" or self.cfg["Category"] == rom.category:
|
||||||
|
if self.cfg["ShowClones"] or rom.cloneof is None:
|
||||||
|
if not self.hide_mature or '* Mature *' not in rom.category:
|
||||||
|
self.roms.append(rom)
|
||||||
|
|
||||||
|
if self.cfg["Sort"] == "Name Asc":
|
||||||
|
self.roms.sort(key=lambda x: x.description.lower(), reverse=False)
|
||||||
|
elif self.cfg["Sort"] == "Name Dec":
|
||||||
|
self.roms.sort(key=lambda x: x.description.lower(), reverse=True)
|
||||||
|
elif self.cfg["Sort"] == "Year Asc":
|
||||||
|
self.roms.sort(key=lambda x: x.year.lower(), reverse=False)
|
||||||
|
elif self.cfg["Sort"] == "Year Dec":
|
||||||
|
self.roms.sort(key=lambda x: x.year.lower(), reverse=True)
|
||||||
|
|
||||||
|
self.len = len(self.roms)
|
||||||
|
|
||||||
|
if self.hide_mature:
|
||||||
|
self.categories = []
|
||||||
|
for category in self.all_categories:
|
||||||
|
if '* Mature *' not in category:
|
||||||
|
self.categories.append(category)
|
||||||
|
else:
|
||||||
|
self.categories = self.all_categories
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
''' Parse xml '''
|
||||||
|
|
||||||
|
mame_version = self.get_mame_version()
|
||||||
|
xmlfile = os.path.join(self.emulator_dir, 'mame.xml')
|
||||||
|
datfile = os.path.join(self.data_dir, 'mame.dat')
|
||||||
|
|
||||||
|
if self.cfg['Version'] != mame_version:
|
||||||
|
self.cfg['Version'] = mame_version
|
||||||
|
|
||||||
|
if os.path.isfile(xmlfile):
|
||||||
|
os.unlink(xmlfile)
|
||||||
|
|
||||||
|
if os.path.isfile(datfile):
|
||||||
|
with open(datfile, 'rb') as i:
|
||||||
|
temp_mame_version = pickle.load(i)
|
||||||
|
|
||||||
|
if temp_mame_version == mame_version:
|
||||||
|
self.all_categories = pickle.load(i)
|
||||||
|
self.all_roms = pickle.load(i)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
os.unlink(datfile, 'mame.dat')
|
||||||
|
|
||||||
|
tempcat = {}
|
||||||
|
catfile = open(os.path.join(self.emulator_dir, 'catver.ini'))
|
||||||
|
|
||||||
|
found = False
|
||||||
|
for line in catfile:
|
||||||
|
line = line.strip()
|
||||||
|
if line == '[VerAdded]':
|
||||||
|
break
|
||||||
|
if line == '[Category]':
|
||||||
|
found = True
|
||||||
|
if found and line != '' and line[0] != ';' and line[0] != '[':
|
||||||
|
# zwackery=Platform / Run Jump
|
||||||
|
game, category = line.split('=')
|
||||||
|
tempcat[game] = category
|
||||||
|
if category not in self.all_categories:
|
||||||
|
self.all_categories.append(category)
|
||||||
|
self.catdict[category] = 0
|
||||||
|
|
||||||
|
self.all_categories.sort()
|
||||||
|
self.all_categories.insert(0, 'All Games')
|
||||||
|
self.all_categories.append('Unknown')
|
||||||
|
self.catdict['All Games'] = 1
|
||||||
|
self.catdict['Unknown'] = 0
|
||||||
|
|
||||||
|
if not os.path.isfile(xmlfile):
|
||||||
|
try:
|
||||||
|
with open(xmlfile, 'w') as out:
|
||||||
|
retcode = subprocess.call(
|
||||||
|
[self.cfg['EXE'], '-listxml'], stdout=out)
|
||||||
|
if retcode != 0:
|
||||||
|
try:
|
||||||
|
os.unlink(xmlfile)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
except OSError:
|
||||||
|
try:
|
||||||
|
os.unlink(xmlfile)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
|
|
||||||
|
with open(os.path.join(self.data_dir, 'mame_exclude.txt'), 'rt') as f:
|
||||||
|
exclude = f.readlines()
|
||||||
|
exclude = [x.strip() for x in exclude]
|
||||||
|
|
||||||
|
tree = etree.parse(xmlfile)
|
||||||
|
|
||||||
|
rom = None
|
||||||
|
for child in tree.getiterator():
|
||||||
|
if child.tag == 'machine':
|
||||||
|
if rom:
|
||||||
|
if rom.category not in exclude:
|
||||||
|
self.all_roms.append(rom)
|
||||||
|
self.catdict[rom.category] += 1
|
||||||
|
rom = None
|
||||||
|
if 'runnable' in child.attrib and child.attrib['runnable'] == 'yes':
|
||||||
|
rom = MameROM()
|
||||||
|
rom.name = child.attrib['name']
|
||||||
|
else:
|
||||||
|
rom = None
|
||||||
|
if rom and 'cloneof' in child.attrib:
|
||||||
|
rom.cloneof = child.attrib['cloneof']
|
||||||
|
if rom and rom.name in tempcat:
|
||||||
|
rom.category = tempcat[rom.name]
|
||||||
|
elif rom and rom.cloneof in tempcat:
|
||||||
|
rom.category = tempcat[rom.cloneof]
|
||||||
|
elif rom:
|
||||||
|
rom.category = 'Unknown'
|
||||||
|
elif rom and child.tag == 'description':
|
||||||
|
rom.description = child.text
|
||||||
|
elif rom and child.tag == 'year':
|
||||||
|
rom.year = child.text
|
||||||
|
elif rom and child.tag == 'manufacturer':
|
||||||
|
rom.manufacturer = child.text
|
||||||
|
elif rom and child.tag == 'driver':
|
||||||
|
rom.status = child.attrib['status']
|
||||||
|
|
||||||
|
if rom:
|
||||||
|
if rom.category not in exclude:
|
||||||
|
self.all_roms.append(rom)
|
||||||
|
|
||||||
|
for cat in self.catdict:
|
||||||
|
if self.catdict[cat] == 0:
|
||||||
|
del self.all_categories[self.all_categories.index(cat)]
|
||||||
|
|
||||||
|
with open(datfile, 'wb') as output:
|
||||||
|
pickle.dump(mame_version, output)
|
||||||
|
pickle.dump(self.all_categories, output)
|
||||||
|
pickle.dump(self.all_roms, output)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_mame_version(self):
|
||||||
|
mamerun = subprocess.run(
|
||||||
|
[self.cfg['EXE'], '-?'], stdout=subprocess.PIPE)
|
||||||
|
output = mamerun.stdout.decode('utf-8')
|
||||||
|
output = output[output.find('v'):]
|
||||||
|
output = output[:output.find(' ')]
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def get_info(self, rom):
|
||||||
|
if not self.info:
|
||||||
|
return
|
||||||
|
|
||||||
|
search = re.search("\\$info=%s" % rom.name, self.info)
|
||||||
|
|
||||||
|
if not search:
|
||||||
|
search = re.search("\\$info=%s" % rom.cloneof, self.info)
|
||||||
|
if not search:
|
||||||
|
return None
|
||||||
|
|
||||||
|
start = search.start()
|
||||||
|
|
||||||
|
info = self.info[start:self.info.find('$end', start)].splitlines()
|
||||||
|
|
||||||
|
for i in range(len(info) - 1, -1, -1):
|
||||||
|
if info[i].startswith('$'):
|
||||||
|
del info[i]
|
||||||
|
|
||||||
|
while info[0].strip() == '':
|
||||||
|
del info[0]
|
||||||
|
|
||||||
|
while info[len(info) - 1].strip() == '':
|
||||||
|
del info[len(info) - 1]
|
||||||
|
|
||||||
|
return '\n'.join(info)
|
||||||
|
|
||||||
|
def get_history(self, rom):
|
||||||
|
if not self.history:
|
||||||
|
return
|
||||||
|
|
||||||
|
search = re.search("\\$info=%s" % rom.name, self.history)
|
||||||
|
|
||||||
|
if not search:
|
||||||
|
search = re.search("\\$info=%s" % rom.cloneof, self.history)
|
||||||
|
if not search:
|
||||||
|
return None
|
||||||
|
|
||||||
|
start = search.start()
|
||||||
|
|
||||||
|
info = self.history[start:self.history.find('$end',
|
||||||
|
start)].splitlines()
|
||||||
|
|
||||||
|
for i in range(len(info) - 1, -1, -1):
|
||||||
|
if info[i].startswith('$'):
|
||||||
|
del info[i]
|
||||||
|
|
||||||
|
while info[0].strip() == '':
|
||||||
|
del info[0]
|
||||||
|
|
||||||
|
while info[len(info) - 1].strip() == '':
|
||||||
|
del info[len(info) - 1]
|
||||||
|
|
||||||
|
return '\n'.join(info)
|
||||||
|
|
||||||
|
def get_snap(self, rom, type_of_image): # pylint : disable=R0912
|
||||||
|
data = None
|
||||||
|
image_file = None
|
||||||
|
image_dir = None
|
||||||
|
|
||||||
|
if type_of_image == 'snap':
|
||||||
|
image_dir = self.snapdir
|
||||||
|
image_file = self.snapfile
|
||||||
|
if type_of_image == 'title':
|
||||||
|
image_dir = self.titlesdir
|
||||||
|
image_file = self.titlesfile
|
||||||
|
if type_of_image == 'marquee':
|
||||||
|
image_dir = self.marqueesdir
|
||||||
|
image_file = self.marqueesfile
|
||||||
|
|
||||||
|
if image_file:
|
||||||
|
try:
|
||||||
|
data = image_file.read(rom.name + ".png")
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
data = image_file.read(rom.cloneof + '.png')
|
||||||
|
except:
|
||||||
|
data = None
|
||||||
|
elif image_dir:
|
||||||
|
fn = os.path.join(image_dir, rom.name + '.png')
|
||||||
|
if os.path.isfile(fn):
|
||||||
|
with open(fn, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
elif rom.cloneof:
|
||||||
|
fn = os.path.join(image_dir, rom.cloneof + '.png')
|
||||||
|
if os.path.isfile(fn):
|
||||||
|
with open(fn, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_artwork(self, rom):
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for item in self.artwork_dirs:
|
||||||
|
title = item
|
||||||
|
dof = self.artwork_dirs[title]['dof']
|
||||||
|
|
||||||
|
if self.artwork_dirs[title]['type'] == 'dir':
|
||||||
|
fn = None
|
||||||
|
if glob('%s/**/%s.*' % (dof, rom.name), recursive=True) != []:
|
||||||
|
fn = glob(
|
||||||
|
'%s/**/%s.*' % (dof, rom.name), recursive=True)[0]
|
||||||
|
elif glob(
|
||||||
|
'%s/**/%s.*' %
|
||||||
|
(dof, rom.cloneof), recursive=True) != []:
|
||||||
|
fn = glob(
|
||||||
|
'%s/**/%s.*' % (dof, rom.cloneof), recursive=True)[0]
|
||||||
|
if fn:
|
||||||
|
data = None
|
||||||
|
if os.path.splitext(fn)[1].lower() in [
|
||||||
|
'.png', '.jpg', '.jpeg'
|
||||||
|
]:
|
||||||
|
with open(fn, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
if data:
|
||||||
|
result.append([title, data])
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
try:
|
||||||
|
with zipfile.ZipFile(dof) as f:
|
||||||
|
data = f.read('%s.png' % rom.name)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
with zipfile.ZipFile(dof) as f:
|
||||||
|
data = f.read('%s.png' % rom.cloneof)
|
||||||
|
except:
|
||||||
|
data = None
|
||||||
|
|
||||||
|
if data:
|
||||||
|
result.append([title, data])
|
||||||
|
|
||||||
|
return result
|
||||||
281
mess.py
Normal file
281
mess.py
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from xml.sax.saxutils import unescape
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
|
||||||
|
class MessROM(object):
|
||||||
|
''' One Mame Software List ROM '''
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.name = ''
|
||||||
|
self.cloneof = None
|
||||||
|
self.description = ''
|
||||||
|
self.year = ''
|
||||||
|
self.manufacturer = ''
|
||||||
|
self.category = ''
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.description
|
||||||
|
# return '["%s", "%s", "%s", "%s", "%s" "%s", "%s"]' % (
|
||||||
|
# self.name, self.cloneof, self.description, self.year,
|
||||||
|
# self.manufacturer, self.status, self.category)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s("%s", "%s", "%s", "%s", "%s", "%s")' % (
|
||||||
|
self.__class__.__name__, self.name, self.cloneof, self.description,
|
||||||
|
self.year, self.manufacturer, self.category)
|
||||||
|
|
||||||
|
|
||||||
|
class MessROMs(object):
|
||||||
|
''' A Collection of MAME Software List Roms '''
|
||||||
|
|
||||||
|
def __init__(self, data_dir, cfg):
|
||||||
|
self.all_roms = []
|
||||||
|
self.roms = []
|
||||||
|
self.categories = []
|
||||||
|
self.all_categories = []
|
||||||
|
self.catdict = {}
|
||||||
|
self.len = 0
|
||||||
|
self.data_dir = data_dir
|
||||||
|
self.cfg = cfg
|
||||||
|
self.emulator_dir = os.path.split(cfg['EXE'])[0]
|
||||||
|
|
||||||
|
self.parse()
|
||||||
|
self.filter()
|
||||||
|
|
||||||
|
self.snapdir = os.path.join(self.emulator_dir, 'snap')
|
||||||
|
if os.path.isfile(os.path.join(self.snapdir, 'snap.zip')):
|
||||||
|
self.snapfile = zipfile.ZipFile(
|
||||||
|
os.path.join(self.snapdir, "snap.zip"))
|
||||||
|
else:
|
||||||
|
self.snapfile = None
|
||||||
|
self.snapdir = os.path.join(self.snapdir, 'snap')
|
||||||
|
|
||||||
|
infofn = os.path.join(self.emulator_dir, 'messinfo.dat')
|
||||||
|
if not os.path.isfile(infofn):
|
||||||
|
self.info = None
|
||||||
|
else:
|
||||||
|
with open(infofn, 'rt', encoding='latin1') as f:
|
||||||
|
self.info = f.read()
|
||||||
|
|
||||||
|
historyfn = os.path.join(self.emulator_dir, 'history.dat')
|
||||||
|
if not os.path.isfile(historyfn):
|
||||||
|
self.history = None
|
||||||
|
else:
|
||||||
|
with open(historyfn, 'rt', encoding='utf-8') as f:
|
||||||
|
self.history = f.read()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.len
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.roms[item]
|
||||||
|
|
||||||
|
def filter(self):
|
||||||
|
self.roms = []
|
||||||
|
|
||||||
|
for rom in self.all_roms:
|
||||||
|
if self.cfg["Category"] == "All Games" or self.cfg["Category"] == rom.category:
|
||||||
|
if self.cfg["ShowClones"] or rom.cloneof is None:
|
||||||
|
self.roms.append(rom)
|
||||||
|
|
||||||
|
if self.cfg["Sort"] == "Name Asc":
|
||||||
|
self.roms.sort(key=lambda x: x.description.lower(), reverse=False)
|
||||||
|
elif self.cfg["Sort"] == "Name Dec":
|
||||||
|
self.roms.sort(key=lambda x: x.description.lower(), reverse=True)
|
||||||
|
elif self.cfg["Sort"] == "Year Asc":
|
||||||
|
self.roms.sort(key=lambda x: x.year.lower(), reverse=False)
|
||||||
|
elif self.cfg["Sort"] == "Year Dec":
|
||||||
|
self.roms.sort(key=lambda x: x.year.lower(), reverse=True)
|
||||||
|
|
||||||
|
self.len = len(self.roms)
|
||||||
|
|
||||||
|
self.categories = self.all_categories
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
""" Parse xml """
|
||||||
|
|
||||||
|
mess_version = self.get_mess_version()
|
||||||
|
datfile = os.path.join(self.data_dir, 'mess.dat')
|
||||||
|
|
||||||
|
if self.cfg['Version'] != mess_version:
|
||||||
|
self.cfg['Version'] = mess_version
|
||||||
|
|
||||||
|
if os.path.isfile(datfile):
|
||||||
|
os.unlink(datfile)
|
||||||
|
|
||||||
|
if os.path.isfile(datfile):
|
||||||
|
with open(datfile, 'rb') as i:
|
||||||
|
temp_mess_version = pickle.load(i)
|
||||||
|
|
||||||
|
if temp_mess_version == mess_version:
|
||||||
|
self.all_categories = pickle.load(i)
|
||||||
|
self.all_roms = pickle.load(i)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
os.unlink(datfile)
|
||||||
|
|
||||||
|
self.all_categories = []
|
||||||
|
self.catdict = {}
|
||||||
|
|
||||||
|
files = glob.glob(os.path.join(self.emulator_dir, "hash", '*.xml'))
|
||||||
|
for file in files:
|
||||||
|
with open(file, 'rt', encoding='utf-8') as f:
|
||||||
|
line = None
|
||||||
|
while True:
|
||||||
|
line = f.readline().strip()
|
||||||
|
if line.startswith('<softwarelist name'):
|
||||||
|
break
|
||||||
|
self.all_categories.append(
|
||||||
|
unescape(line[line.find('ion=') + 5:-2]))
|
||||||
|
self.catdict[self.all_categories[-1]] = file
|
||||||
|
self.catdict[file] = self.all_categories[-1]
|
||||||
|
# TODO: Fix me for emulator names
|
||||||
|
# <softwarelist name="n64" description="Nintendo 64 cartridges">
|
||||||
|
self.all_categories.sort()
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
|
for xmlfile in files:
|
||||||
|
tree = etree.parse(xmlfile)
|
||||||
|
|
||||||
|
rom = None
|
||||||
|
for child in tree.getiterator():
|
||||||
|
if child.tag == 'software':
|
||||||
|
if rom:
|
||||||
|
rom.category = self.catdict[xmlfile]
|
||||||
|
self.all_roms.append(rom)
|
||||||
|
rom = None
|
||||||
|
if 'supported' not in child.attrib or child.attrib['supported'] != 'no':
|
||||||
|
rom = MessROM()
|
||||||
|
rom.name = child.attrib['name']
|
||||||
|
else:
|
||||||
|
rom = None
|
||||||
|
if rom and 'cloneof' in child.attrib:
|
||||||
|
rom.cloneof = child.attrib['cloneof']
|
||||||
|
elif rom and child.tag == 'description':
|
||||||
|
rom.description = child.text
|
||||||
|
elif rom and child.tag == 'year':
|
||||||
|
rom.year = child.text
|
||||||
|
elif rom and child.tag == 'publisher':
|
||||||
|
rom.manufacturer = child.text
|
||||||
|
|
||||||
|
if rom:
|
||||||
|
rom.category = self.catdict[xmlfile]
|
||||||
|
self.all_roms.append(rom)
|
||||||
|
|
||||||
|
self.all_categories.insert(0, 'All Games')
|
||||||
|
self.all_categories.append('Unknown')
|
||||||
|
|
||||||
|
with open(datfile, 'wb') as output:
|
||||||
|
pickle.dump(mess_version, output)
|
||||||
|
pickle.dump(self.all_categories, output)
|
||||||
|
pickle.dump(self.all_roms, output)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_mess_version(self):
|
||||||
|
mamerun = subprocess.run(
|
||||||
|
[self.cfg['EXE'], '-?'], stdout=subprocess.PIPE)
|
||||||
|
output = mamerun.stdout.decode('utf-8')
|
||||||
|
output = output[output.find('v'):]
|
||||||
|
output = output[:output.find(' ')]
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def get_snap(self, rom, type_of_image): # pylint : disable=R0912
|
||||||
|
data = None
|
||||||
|
image_file = None
|
||||||
|
image_dir = None
|
||||||
|
|
||||||
|
if type_of_image == 'snap':
|
||||||
|
image_dir = self.snapdir
|
||||||
|
image_file = self.snapfile
|
||||||
|
|
||||||
|
if image_file:
|
||||||
|
try:
|
||||||
|
data = image_file.read(rom.name + ".png")
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
data = image_file.read(rom.cloneof + '.png')
|
||||||
|
except:
|
||||||
|
data = None
|
||||||
|
elif image_dir:
|
||||||
|
fn = os.path.join(image_dir, rom.name + '.png')
|
||||||
|
if os.path.isfile(fn):
|
||||||
|
with open(fn, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
elif rom.cloneof:
|
||||||
|
fn = os.path.join(image_dir, rom.cloneof + '.png')
|
||||||
|
if os.path.isfile(fn):
|
||||||
|
with open(fn, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_info(self, rom):
|
||||||
|
if not self.info:
|
||||||
|
return
|
||||||
|
|
||||||
|
search = re.search("\\$info=%s" % rom.name, self.info)
|
||||||
|
|
||||||
|
if not search:
|
||||||
|
search = re.search("\\$info=%s" % rom.cloneof, self.info)
|
||||||
|
if not search:
|
||||||
|
return None
|
||||||
|
|
||||||
|
start = search.start()
|
||||||
|
|
||||||
|
info = self.info[start:self.info.find('$end', start)].splitlines()
|
||||||
|
|
||||||
|
for i in range(len(info) - 1, -1, -1):
|
||||||
|
if info[i].startswith('$'):
|
||||||
|
del info[i]
|
||||||
|
|
||||||
|
while info[0].strip() == '':
|
||||||
|
del info[0]
|
||||||
|
|
||||||
|
while info[len(info) - 1].strip() == '':
|
||||||
|
del info[len(info) - 1]
|
||||||
|
|
||||||
|
return '\n'.join(info)
|
||||||
|
|
||||||
|
def get_history(self, rom):
|
||||||
|
print(rom.name)
|
||||||
|
if not self.history:
|
||||||
|
return
|
||||||
|
|
||||||
|
search = re.search("\\$info=%s" % rom.name, self.history)
|
||||||
|
|
||||||
|
if not search:
|
||||||
|
search = re.search("\\$info=%s" % rom.cloneof, self.history)
|
||||||
|
if not search:
|
||||||
|
return None
|
||||||
|
|
||||||
|
start = search.start()
|
||||||
|
|
||||||
|
info = self.history[start:self.history.find('$end',
|
||||||
|
start)].splitlines()
|
||||||
|
|
||||||
|
for i in range(len(info) - 1, -1, -1):
|
||||||
|
if info[i].startswith('$'):
|
||||||
|
del info[i]
|
||||||
|
|
||||||
|
while info[0].strip() == '':
|
||||||
|
del info[0]
|
||||||
|
|
||||||
|
while info[len(info) - 1].strip() == '':
|
||||||
|
del info[len(info) - 1]
|
||||||
|
|
||||||
|
return '\n'.join(info)
|
||||||
11
mfe.py
Normal file
11
mfe.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import app
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# try:
|
||||||
|
# startat = int(sys.argv[1])
|
||||||
|
# except:
|
||||||
|
# startat = None
|
||||||
|
|
||||||
|
application = app.app()
|
||||||
|
|
||||||
|
application.run()
|
||||||
34
mfe_mac.ini
Normal file
34
mfe_mac.ini
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
ResolutionX = 1024
|
||||||
|
ResolutionY = 768
|
||||||
|
FullScreen = False
|
||||||
|
Font = microsoftsansserif
|
||||||
|
FontSize = 13
|
||||||
|
QuitKeyPresses = 1
|
||||||
|
ScanLines = True
|
||||||
|
Key_Up = K_UP,
|
||||||
|
Key_Down = K_DOWN,
|
||||||
|
Key_PgUp = K_LEFT, K_PAGEUP
|
||||||
|
Key_PgDn = K_RIGHT, K_PAGEDOWN
|
||||||
|
Key_Home = K_HOME,
|
||||||
|
Key_End = K_END,
|
||||||
|
Key_Select = K_RETURN, K_1
|
||||||
|
Key_GameInfo = K_5,
|
||||||
|
Key_GameHistory = K_6,
|
||||||
|
Key_Popup = K_2,
|
||||||
|
AlwaysChangeSnap = True
|
||||||
|
HideMature = True
|
||||||
|
Emulator = M.A.M.E.
|
||||||
|
Key_ShowArtwork = ,
|
||||||
|
[M.A.M.E.]
|
||||||
|
EXE = /Users/rich/git/mfe/mame/mame64
|
||||||
|
Version = v0.190
|
||||||
|
ShowClones = False
|
||||||
|
Category = All Games
|
||||||
|
GameAtTop = 0
|
||||||
|
CurrentGame = 6
|
||||||
|
EmulatorType = MAME
|
||||||
|
StatusFilter = good,
|
||||||
|
Sort = Name Asc
|
||||||
|
SnapDir = None
|
||||||
|
CSVFile = None
|
||||||
|
ArtworkDirs = ,
|
||||||
4
readme.txt
Normal file
4
readme.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
MFE
|
||||||
|
|
||||||
|
Mac Build
|
||||||
|
python3 setup.py bdist_mac --iconfile=images/arcade.icns
|
||||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
pygame>=1.9.3
|
||||||
|
configobj>=5.0.6
|
||||||
|
appdirs>=1.4.3
|
||||||
|
lxml>=3.8.0
|
||||||
|
titlecase>=0.11.0
|
||||||
|
cx_Freeze>=5.0.2
|
||||||
23
setup.py
Normal file
23
setup.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
from cx_Freeze import setup, Executable
|
||||||
|
|
||||||
|
build_exe_options = {
|
||||||
|
"packages": ["os", "pygame", "lxml"],
|
||||||
|
"excludes": ["tkinter", "numpy"],
|
||||||
|
"include_files": ["arcade.png"]
|
||||||
|
}
|
||||||
|
|
||||||
|
base = None
|
||||||
|
if sys.platform == "win32":
|
||||||
|
base = "Win32GUI"
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="mfe",
|
||||||
|
version="0.1",
|
||||||
|
description="MAME FrontEnd",
|
||||||
|
options={"build_exe": build_exe_options},
|
||||||
|
executables=[
|
||||||
|
Executable(
|
||||||
|
"mfe.py", base=base, icon=os.path.join('images', 'arcade.ico'))
|
||||||
|
])
|
||||||
135
utils.py
Normal file
135
utils.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
from itertools import chain
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
import pygame.locals
|
||||||
|
|
||||||
|
|
||||||
|
def get_pygame_keydict():
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
pygame_keys = [
|
||||||
|
item for item in dir(pygame.locals) if item.startswith('K_')
|
||||||
|
]
|
||||||
|
|
||||||
|
for key in pygame_keys:
|
||||||
|
result[key] = getattr(pygame.locals, key)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_keys(keys_dict, a):
|
||||||
|
result = []
|
||||||
|
for key in a:
|
||||||
|
result.append(keys_dict[key])
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def aspect_scale(img, bx, by):
|
||||||
|
""" Scales 'img' to fit into box bx/by.
|
||||||
|
This method will retain the original image's aspect ratio """
|
||||||
|
ix, iy = img.get_size()
|
||||||
|
if ix > iy:
|
||||||
|
# fit to width
|
||||||
|
scale_factor = bx / float(ix)
|
||||||
|
sy = scale_factor * iy
|
||||||
|
if sy > by:
|
||||||
|
scale_factor = by / float(iy)
|
||||||
|
sx = scale_factor * ix
|
||||||
|
sy = by
|
||||||
|
else:
|
||||||
|
sx = bx
|
||||||
|
else:
|
||||||
|
# fit to height
|
||||||
|
scale_factor = by / float(iy)
|
||||||
|
sx = scale_factor * ix
|
||||||
|
if sx > bx:
|
||||||
|
scale_factor = bx / float(ix)
|
||||||
|
sx = bx
|
||||||
|
sy = scale_factor * iy
|
||||||
|
else:
|
||||||
|
sy = by
|
||||||
|
|
||||||
|
return pygame.transform.scale(img, (int(sx), int(sy)))
|
||||||
|
|
||||||
|
|
||||||
|
def image_from_data(data, image_size):
|
||||||
|
if data:
|
||||||
|
with io.BytesIO(data) as f:
|
||||||
|
surface = pygame.image.load(f)
|
||||||
|
surface = aspect_scale(surface, image_size[0], image_size[1])
|
||||||
|
else:
|
||||||
|
surface = pygame.Surface(image_size) # pylint: disable=E1121
|
||||||
|
|
||||||
|
return surface
|
||||||
|
|
||||||
|
|
||||||
|
def addscanlines(surface):
|
||||||
|
width = surface.get_width()
|
||||||
|
for y in range(surface.get_height()):
|
||||||
|
if y % 2:
|
||||||
|
pygame.draw.line(surface, (0, 0, 0), (0, y), (width, y))
|
||||||
|
|
||||||
|
return surface
|
||||||
|
|
||||||
|
|
||||||
|
def truncline(text, font, maxwidth):
|
||||||
|
real = len(text)
|
||||||
|
stext = text
|
||||||
|
l = font.size(text)[0]
|
||||||
|
cut = 0
|
||||||
|
a = 0
|
||||||
|
done = 1
|
||||||
|
while l > maxwidth:
|
||||||
|
a = a + 1
|
||||||
|
n = text.rsplit(None, a)[0]
|
||||||
|
if stext == n:
|
||||||
|
cut += 1
|
||||||
|
stext = n[:-cut]
|
||||||
|
else:
|
||||||
|
stext = n
|
||||||
|
l = font.size(stext)[0]
|
||||||
|
real = len(stext)
|
||||||
|
done = 0
|
||||||
|
return real, done, stext
|
||||||
|
|
||||||
|
|
||||||
|
def wrapline(text, font, maxwidth):
|
||||||
|
done = 0
|
||||||
|
wrapped = []
|
||||||
|
|
||||||
|
while not done:
|
||||||
|
nl, done, stext = truncline(text, font, maxwidth)
|
||||||
|
wrapped.append(stext.strip())
|
||||||
|
text = text[nl:]
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_multi_line(text, font, maxwidth):
|
||||||
|
""" returns text taking new lines into account.
|
||||||
|
"""
|
||||||
|
if type(text) is str:
|
||||||
|
lines = chain(*(wrapline(line, font, maxwidth)
|
||||||
|
for line in text.splitlines()))
|
||||||
|
else:
|
||||||
|
lines = chain(*(wrapline(line, font, maxwidth) for line in text))
|
||||||
|
|
||||||
|
return list(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def run_emulator(emu_type, exe, rom):
|
||||||
|
old_path = os.getcwd()
|
||||||
|
os.chdir(os.path.split(exe)[0])
|
||||||
|
|
||||||
|
if emu_type == 'MAME':
|
||||||
|
subprocess.run([exe, rom])
|
||||||
|
elif emu_type == 'MESS':
|
||||||
|
subprocess.run([exe, rom])
|
||||||
|
elif emu_type == 'CSV':
|
||||||
|
subprocess.run([exe.replace('<romname>', rom)])
|
||||||
|
|
||||||
|
os.chdir(old_path)
|
||||||
|
|
||||||
|
return True
|
||||||
Reference in New Issue
Block a user