Skip to content

Commit fcb558d

Browse files
committed
version 1.1, adding support for a rules/destination engine
1 parent af7b10c commit fcb558d

File tree

6 files changed

+273
-23
lines changed

6 files changed

+273
-23
lines changed

README.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# RomLoader for sd2snes
2+
3+
Lets you easily load a ROM onto your sd2snes by using usb2snes! Great for Rando runners, and especially great for MSU-1 users.
4+
5+
## Usage
6+
7+
Put romloader.exe in a place that is likely not to change. Copy romloader.yaml to the same directory as your romloader.exe if you want to customize the behavior. Have Windows open the .sfc or .smc file with romloader.exe!
8+
9+
You'll need usb2snes firmware and the usb2snes software running on your PC. You can find it at https://github.com/RedGuyyyy/sd2snes/releases/tag/usb2snes_v9
10+
11+
## Configuration
12+
13+
By default, without a config file, it'll use /romloader for all files.
14+
15+
You can customize the behavior though with a romloader.yaml file in the same directory.
16+
17+
`device` is the usb2snes device you want romloader to use, leave this out to have it use the first one it finds (recommended)
18+
19+
`default_destination` is the directory where ROMs will be put on your SD card if no rules are matched. If you change this, make sure the directory exists on your SD card.
20+
21+
`rules` this is the meat and potatoes. You can create rules for each of your games here.
22+
23+
### rules section
24+
25+
The `rules` section has an arbitrary name, and then under that a `name_pattern` key which is an string to wildcard match to the filename.
26+
27+
Each rule has a `destinations` key which is a yaml list of various destinations for your matched file. The first item in the list will be the default.
28+
If `romname` is not found as a key within the list, it will copy the name of the file as-is to the SD card. If only one destination for the rule is specified, it will use that automatically. If more than one destination is specified, it will prompt you.
29+
30+
Example of the prompt:
31+
32+
```
33+
Attaching to first device found.
34+
Attached to device "SD2SNES COM3"
35+
----------------------------
36+
0 - default
37+
1 - aLttPArranged
38+
2 - aLttPDeMastered
39+
3 - EpicAnniversaryOST
40+
4 - SuperMarioRPG
41+
5 - Zelda3FMProject
42+
6 - ZeldaMetal
43+
7 - ZeldaReOrchestrated
44+
What destination (enter to chose 0)?
45+
```
46+
47+
48+
### example config file
49+
```yaml
50+
### device option can be commented out to have this tool use the first sd2snes device it finds, which in most cases is fine.
51+
### If you have multiple sd2snes units connected, you'll need to specify the one you want to use.
52+
53+
# device: "COM3"
54+
55+
### default directory to put the ROM if no rules are matched.
56+
### Make sure this directory exists on your SD card (will try to create it if /romloader)
57+
default_destination: "/romloader"
58+
59+
### a set of rules to use to put certain ROMs in certain locations, such as your randomizer ROMs, useful for MSU1 users
60+
rules:
61+
alttpr:
62+
name_pattern: "ALttP - VT_*" # look for an input ROM that matches this name
63+
destinations:
64+
- name: default
65+
path: "/LinkToThePast/Transferred"
66+
- name: aLttPArranged
67+
path: "/LinkToThePast/MSU1/aLttPArranged"
68+
romname: alttp_msu.sfc
69+
- name: aLttPDeMastered
70+
path: "/LinkToThePast/MSU1/aLttPDeMastered"
71+
romname: loz3-demaster.sfc
72+
- name: EpicAnniversaryOST
73+
path: "/LinkToThePast/MSU1/EpicAnniversaryOST"
74+
romname: loz3-dx.sfc
75+
- name: SuperMarioRPG
76+
path: "/LinkToThePast/MSU1/SuperMarioRPG"
77+
romname: alttp_msu.sfc
78+
- name: Zelda3FMProject
79+
path: "/LinkToThePast/MSU1/Zelda3FMProject"
80+
romname: track.sfc
81+
- name: ZeldaMetal
82+
path: "/LinkToThePast/MSU1/ZeldaMetal"
83+
romname: track.sfc
84+
- name: ZeldaReOrchestrated
85+
path: "/LinkToThePast/MSU1/ZeldaReOrchestrated"
86+
romname: alttp_msu.sfc
87+
smz3:
88+
name_pattern: "SMALttP - sm-*"
89+
destinations:
90+
- name: default
91+
path: "/SMZ3/roms"
92+
smw:
93+
name_pattern: "smw-*"
94+
destinations:
95+
- name: default
96+
path: "/SuperMarioWorld/Rando"
97+
supermetroiditem:
98+
name_pattern: "Item Randomizer *"
99+
destinations:
100+
- name: default
101+
path: "/SuperMetroid/Rando"
102+
supermetroidvaria:
103+
name_pattern: "VARIA_Randomizer_*"
104+
destinations:
105+
- name: default
106+
path: "/SuperMetroid/Rando"
107+
```
-6.8 KB
Binary file not shown.

romloader.spec

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
block_cipher = None
44

5-
6-
a = Analysis(['romloader.py'],
5+
a = Analysis(['src/romloader.py'],
76
pathex=['D:\\Code\\RomLoader'],
87
binaries=[],
98
datas=[],

py2snes/__init__.py renamed to src/py2snes/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class usb2snes():
1111
def __init__(self):
1212
self.conn = websocket.create_connection("ws://localhost:8080")
1313
self.attached = False
14-
14+
1515
def close(self):
1616
self.conn.close()
1717

@@ -39,6 +39,7 @@ def Attach(self, com=None):
3939
}
4040
self.conn.send(json.dumps(cmd))
4141
self.attached = True
42+
return com
4243
else:
4344
self.attached = False
4445
raise usb2snesException("Unable to find sd2snes device {com}. Make sure your sd2snes is powered on and usb2snes firmware installed.".format(

src/romloader.py

Lines changed: 105 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,111 @@
11
import sys
22
import os
33
import py2snes
4+
import yaml
5+
import fnmatch
46
from time import sleep
57

8+
### load the configuration file (if it exists, otherwise use default config)
9+
scriptpath=os.path.dirname(sys.argv[0])
10+
611
try:
7-
rompath=sys.argv[1]
8-
except IndexError:
9-
print('We need a path to the ROM file to load.')
10-
sys.exit()
11-
romname=os.path.basename(rompath)
12-
13-
conn = py2snes.usb2snes()
14-
print("attaching to first sd2snes found")
15-
conn.Attach()
16-
conn.Name('RomLoader')
17-
print("making /romloader directory if it doesn't exist")
18-
conn.MakeDir('/romloader')
19-
print("copying rom")
20-
conn.PutFile(rompath,'/romloader/' + romname)
21-
print("verifying rom copy is complete")
22-
conn.List('/romloader')
23-
print("booting rom")
24-
conn.Boot('/romloader/' + romname)
25-
sleep(10)
26-
conn.close()
12+
with open(scriptpath + "\\romloader.yaml") as configfile:
13+
try:
14+
config = yaml.load(configfile)
15+
except yaml.YAMLError as e:
16+
print(e)
17+
sys.exit(1)
18+
except FileNotFoundError:
19+
try:
20+
with open("romloader.yaml") as configfile:
21+
try:
22+
config = yaml.load(configfile)
23+
except yaml.YAMLError as e:
24+
print(e)
25+
sys.exit(1)
26+
except FileNotFoundError:
27+
config = {"default_destination": '/romloader'}
28+
29+
def main():
30+
try:
31+
rompath=sys.argv[1]
32+
except IndexError:
33+
print('We need a path to the ROM file to load.')
34+
sys.exit(1)
35+
filename=os.path.basename(rompath)
36+
37+
### initiate connection to the websocket server
38+
conn = py2snes.usb2snes()
39+
40+
### Attach to usb2snes, use the device configured if it is set, otherwise have it find the first device.
41+
if "device" in config:
42+
print('Attaching to specified device {device}'.format(
43+
device=config['device']
44+
))
45+
com = conn.Attach(config['device'])
46+
else:
47+
print('Attaching to first device found.')
48+
com = conn.Attach()
49+
print('Attached to device \"{com}\"'.format(
50+
com = com
51+
))
52+
53+
conn.Name('RomLoader')
54+
55+
rule = matchrule(filename)
56+
if rule:
57+
if len(config['rules'][rule]['destinations']) == 1:
58+
path = config['rules'][rule]['destinations'][0]['path']
59+
try:
60+
romname = config['rules'][rule]['destinations'][0]['romname']
61+
except KeyError:
62+
romname = filename
63+
elif len(config['rules'][rule]['destinations']) == 0:
64+
path = config['default_destination']
65+
romname = filename
66+
else:
67+
path, romname = get_destination(rule, filename)
68+
else:
69+
path = config['default_destination']
70+
romname = filename
71+
print("making {path} directory if it doesn't exist".format(
72+
path=path
73+
))
74+
conn.MakeDir('/romloader')
75+
conn.List(path)
76+
print("copying rom to {fullpath}".format(
77+
fullpath=path + '/' + romname
78+
))
79+
conn.PutFile(rompath,path + '/' + romname)
80+
print("verifying rom copy is complete")
81+
conn.List(path)
82+
print("booting rom")
83+
conn.Boot(path + '/' + romname)
84+
conn.close()
85+
86+
def matchrule(name):
87+
if "rules" in config:
88+
for rule in config['rules']:
89+
if fnmatch.fnmatch(name, config['rules'][rule]['name_pattern']):
90+
return rule
91+
else:
92+
return None
93+
94+
def get_destination(rule, romname):
95+
print('----------------------------')
96+
for idx, dest in enumerate(config['rules'][rule]['destinations']):
97+
print(str(idx) + ' - ' + dest['name'])
98+
destination_index = input('What destination (enter to chose 0)? ')
99+
print(destination_index)
100+
if destination_index == '':
101+
destination_index = 0
102+
path = config['rules'][rule]['destinations'][int(destination_index)]['path']
103+
try:
104+
name = config['rules'][rule]['destinations'][int(destination_index)]['romname']
105+
except KeyError:
106+
name = romname
107+
return path, name
108+
109+
sleep(15)
110+
111+
main()

src/romloader.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
### device option can be commented out to have this tool use the first sd2snes device it finds, which in most cases is fine.
2+
### If you have multiple sd2snes units connected, you'll need to specify the one you want to use.
3+
4+
# device: "COM3"
5+
6+
### default directory to put the ROM if no rules are matched.
7+
### Make sure this directory exists on your SD card (will try to create it if /romloader)
8+
default_destination: "/romloader"
9+
10+
### a set of rules to use to put certain ROMs in certain locations, such as your randomizer ROMs, useful for MSU1 users
11+
rules:
12+
alttpr:
13+
name_pattern: "ALttP - VT_*" # look for an input ROM that matches this name
14+
destinations:
15+
- name: default
16+
path: "/LinkToThePast/Transferred"
17+
- name: aLttPArranged
18+
path: "/LinkToThePast/MSU1/aLttPArranged"
19+
romname: alttp_msu.sfc
20+
- name: aLttPDeMastered
21+
path: "/LinkToThePast/MSU1/aLttPDeMastered"
22+
romname: loz3-demaster.sfc
23+
- name: EpicAnniversaryOST
24+
path: "/LinkToThePast/MSU1/EpicAnniversaryOST"
25+
romname: loz3-dx.sfc
26+
- name: SuperMarioRPG
27+
path: "/LinkToThePast/MSU1/SuperMarioRPG"
28+
romname: alttp_msu.sfc
29+
- name: Zelda3FMProject
30+
path: "/LinkToThePast/MSU1/Zelda3FMProject"
31+
romname: track.sfc
32+
- name: ZeldaMetal
33+
path: "/LinkToThePast/MSU1/ZeldaMetal"
34+
romname: track.sfc
35+
- name: ZeldaReOrchestrated
36+
path: "/LinkToThePast/MSU1/ZeldaReOrchestrated"
37+
romname: alttp_msu.sfc
38+
smz3:
39+
name_pattern: "SMALttP - sm-*"
40+
destinations:
41+
- name: default
42+
path: "/SMZ3/roms"
43+
smw:
44+
name_pattern: "smw-*"
45+
destinations:
46+
- name: default
47+
path: "/SuperMarioWorld/Rando"
48+
supermetroiditem:
49+
name_pattern: "Item Randomizer *"
50+
destinations:
51+
- name: default
52+
path: "/SuperMetroid/Rando"
53+
supermetroidvaria:
54+
name_pattern: "VARIA_Randomizer_*"
55+
destinations:
56+
- name: default
57+
path: "/SuperMetroid/Rando"
58+

0 commit comments

Comments
 (0)