added binary mode and "raw" input, opna now supports all 6 instruments
This commit is contained in:
parent
bd7d32812d
commit
874753d4b1
21
README.md
21
README.md
@ -4,15 +4,34 @@ This script converts a [Hoot](http://snesmusic.org/hoot/v2/) memory dump into an
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
`./hootvopm.py -t chip_type input_file`
|
`./hootvopm.py [-b] -t chip_type input_file`
|
||||||
|
|
||||||
|
Input file can also be `-` to read from stdin.
|
||||||
|
|
||||||
Output is dumped straight to stdout; redirect it to a file if you want to save it.
|
Output is dumped straight to stdout; redirect it to a file if you want to save it.
|
||||||
|
|
||||||
|
Pass -b to instead read straight binary data instead of comma-separated values.
|
||||||
|
|
||||||
### Currently-supported chip types
|
### Currently-supported chip types
|
||||||
|
|
||||||
* OPM
|
* OPM
|
||||||
* OPN
|
* OPN
|
||||||
* OPNA (equivalent to OPN)
|
* OPNA (equivalent to OPN)
|
||||||
|
* Raw data in 32-byte groups (see below)
|
||||||
|
|
||||||
|
#### Raw data format
|
||||||
|
| Offset | Value |
|
||||||
|
|--------|-------------|
|
||||||
|
| 0x00 | DT/MUL* |
|
||||||
|
| 0x04 | TL* |
|
||||||
|
| 0x08 | KS/AR* |
|
||||||
|
| 0x0C | AMS-EN/D1R* |
|
||||||
|
| 0x10 | DT2/D2R* |
|
||||||
|
| 0x14 | D1L/RR* |
|
||||||
|
| 0x18 | FL/CON |
|
||||||
|
| 0x19 | AMS/PMS |
|
||||||
|
|
||||||
|
Rows marked with * are groups of 4 bytes, one per operator.
|
||||||
|
|
||||||
## Capturing memory in hoot
|
## Capturing memory in hoot
|
||||||
|
|
||||||
|
157
hootvopm.py
157
hootvopm.py
@ -8,7 +8,7 @@ def Error(*args):
|
|||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
Filename = sys.argv[1]
|
Filename = sys.argv[1]
|
||||||
|
|
||||||
Options, Args = getopt.getopt(sys.argv[1:], "ot:")
|
Options, Args = getopt.getopt(sys.argv[1:], "bot:")
|
||||||
|
|
||||||
def OptionExist(opt):
|
def OptionExist(opt):
|
||||||
global Options
|
global Options
|
||||||
@ -24,22 +24,50 @@ def GetOption(opt):
|
|||||||
if len(Args) >= 1:
|
if len(Args) >= 1:
|
||||||
Filename = Args[0]
|
Filename = Args[0]
|
||||||
else:
|
else:
|
||||||
Error("need a file, genius")
|
Error("no input file")
|
||||||
|
|
||||||
|
BinMode = OptionExist("-b")
|
||||||
DataFile = open(Filename,"r")
|
DataFile = open(Filename,"r" + ("b" if BinMode else "")) if Filename != "-" else sys.stdin
|
||||||
# MiOPMdrv sound bank Paramer Ver2002.04.22
|
# MiOPMdrv sound bank Paramer Ver2002.04.22
|
||||||
# LFO: LFRQ AMD PMD WF NFRQ
|
# LFO: LFRQ AMD PMD WF NFRQ
|
||||||
# @:[Num] [Name]
|
# @:[Num] [Name]
|
||||||
# CH: PAN FL CON AMS PMS SLOT NE
|
# CH: PAN FL CON AMS PMS SLOT NE
|
||||||
# [OPname]: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
|
# [OPname]: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
|
||||||
Data = []
|
Data = [] if not BinMode else DataFile.read()
|
||||||
OpNames = ["M1","C1","M2","C2"]
|
OpNames = ["M1","C1","M2","C2"]
|
||||||
OpIdx = [0,2,1,3]
|
OpIdx = [0,2,1,3]
|
||||||
|
|
||||||
for line in DataFile.readlines():
|
if not BinMode:
|
||||||
for byte in line.strip().split(",")[:16]:
|
for line in DataFile.readlines():
|
||||||
Data.append(int(byte,16))
|
for byte in line.strip().split(",")[:16]:
|
||||||
|
Data.append(int(byte,16))
|
||||||
|
|
||||||
|
def PrintChannel(pan, fl, con, ams, pms, slot, ne):
|
||||||
|
print("CH:", pan, fl, con, ams, pms, slot, ne)
|
||||||
|
|
||||||
|
def PrintOp(op, ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen):
|
||||||
|
print(OpNames[op] + ":", ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen)
|
||||||
|
|
||||||
|
def GetFlCon(b):
|
||||||
|
return (b & 0b00111000) >> 3, (b & 0b00000111)
|
||||||
|
|
||||||
|
def GetAMSPMS(b):
|
||||||
|
return (b & 0b00110000) >> 4, (b & 0b00000111)
|
||||||
|
|
||||||
|
def GetDT1Mul(b):
|
||||||
|
return (b & 0b01110000) >> 4, (b & 0b00001111)
|
||||||
|
|
||||||
|
def GetKSAR(b):
|
||||||
|
return (b & 0b11000000) >> 6, (b & 0b00011111)
|
||||||
|
|
||||||
|
def GetAmsenD1R(b):
|
||||||
|
return (b & 0b10000000) >> 7, (b & 0b00011111)
|
||||||
|
|
||||||
|
def GetDT2D2R(b):
|
||||||
|
return (b & 0b11000000) >> 6, (b & 0b00011111)
|
||||||
|
|
||||||
|
def GetD1LRR(b):
|
||||||
|
return (b & 0b11110000) >> 4, (b & 0b00001111)
|
||||||
|
|
||||||
def PrintOPM(data):
|
def PrintOPM(data):
|
||||||
ne = (data[0xf] & 0b10000000) >> 7
|
ne = (data[0xf] & 0b10000000) >> 7
|
||||||
@ -52,97 +80,96 @@ def PrintOPM(data):
|
|||||||
|
|
||||||
meta = data[0x20 + i : 0x3f + i : 8]
|
meta = data[0x20 + i : 0x3f + i : 8]
|
||||||
|
|
||||||
fl = (meta[0] & 0b00111000) >> 3
|
fl, con = GetFlCon(meta[0])
|
||||||
con = (meta[0] & 0b00000111)
|
|
||||||
|
|
||||||
pms = (meta[3] & 0b01110000) >> 4
|
pms, ams = GetAMSPMS(meta[3])
|
||||||
ams = (meta[3] & 0b00000011)
|
|
||||||
|
|
||||||
print("CH:", pan, fl, con, ams, pms, slot, ne)
|
PrintChannel(pan, fl, con, ams, pms, slot, ne)
|
||||||
|
|
||||||
for op in range(4):
|
for op in range(4):
|
||||||
get = lambda x: data[x + i + OpIdx[op]*8]
|
get = lambda x: data[x + i + OpIdx[op]*8]
|
||||||
|
|
||||||
b = get(0x40)
|
dt1, mul = GetDT1Mul(get(0x40))
|
||||||
dt1 = (b & 0b01110000) >> 4
|
|
||||||
mul = (b & 0b00001111)
|
|
||||||
|
|
||||||
tl = get(0x60)
|
tl = get(0x60)
|
||||||
|
ks, ar = GetKSAR(get(0x80))
|
||||||
|
amsen, d1r = GetAmsenD1R(get(0xa0))
|
||||||
|
dt2, d2r = GetDT2D2R(get(0xc0))
|
||||||
|
d1l, rr = GetD1LRR(get(0xe0))
|
||||||
|
|
||||||
b = get(0x80)
|
PrintOp(op, ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen)
|
||||||
ks = (b & 0b11000000) >> 6
|
|
||||||
ar = (b & 0b00011111)
|
|
||||||
|
|
||||||
b = get(0xa0)
|
|
||||||
amsen = (b & 0b10000000) >> 7
|
|
||||||
d1r = (b & 0b00011111)
|
|
||||||
|
|
||||||
b = get(0xc0)
|
|
||||||
dt2 = (b & 0b11000000) >> 6
|
|
||||||
d2r = (b & 0b00011111)
|
|
||||||
|
|
||||||
b = get(0xe0)
|
|
||||||
d1l = (b & 0b11110000) >> 4
|
|
||||||
rr = (b & 0b00001111)
|
|
||||||
|
|
||||||
print(OpNames[op] + ":", ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen)
|
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
def PrintOPN(data):
|
def PrintOPNBasic(data, opna = False):
|
||||||
slot = 120
|
slot = 120
|
||||||
pan = 64
|
pan = 64
|
||||||
ne = 0 # either i'm blind or opn has no noise enable bit (not that i care either way)
|
ne = 0 # either i'm blind or opn has no noise enable bit (not that i care either way)
|
||||||
# SR in opn parlance is equivalent to D2R in opm, thanks yamaha
|
# SR in opn parlance is equivalent to D2R in opm, thanks yamaha
|
||||||
# likewise, SL -> D1L and DR -> D1R
|
# likewise, SL -> D1L and DR -> D1R
|
||||||
|
ofs = 0x100 if opna else 0
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
print("@:%d Ins%d" % (i, i))
|
n = i + (3 if opna else 0)
|
||||||
|
print("@:%d Ins%d" % (n, n))
|
||||||
print("LFO: 0 0 0 0 0") # no way to get this data... at least for now
|
print("LFO: 0 0 0 0 0") # no way to get this data... at least for now
|
||||||
|
|
||||||
fbcon = data[0xb0 + i]
|
fl, con = GetFlCon(data[ofs + 0xb0 + i])
|
||||||
fl = (fbcon & 0b00111000) >> 3
|
|
||||||
con = (fbcon & 0b00000111)
|
|
||||||
|
|
||||||
sens = data[0xb4 + i]
|
ams, pms = GetAMSPMS(data[ofs + 0xb4 + i])
|
||||||
ams = (sens & 0b00110000) >> 4
|
|
||||||
pms = (sens & 0b00000111)
|
|
||||||
|
|
||||||
print("CH:", pan, fl, con, ams, pms, slot, ne)
|
PrintChannel(pan, fl, con, ams, pms, slot, ne)
|
||||||
|
|
||||||
for op in range(4):
|
for op in range(4):
|
||||||
get = lambda x: data[x + i + OpIdx[op]*4]
|
get = lambda x: data[ofs + x + i + OpIdx[op]*4]
|
||||||
|
|
||||||
b = get(0x30)
|
|
||||||
dt1 = (b & 0b01110000) >> 4
|
|
||||||
mul = (b & 0b00001111)
|
|
||||||
|
|
||||||
|
dt1, mul = GetDT1Mul(get(0x30))
|
||||||
tl = get(0x40)
|
tl = get(0x40)
|
||||||
|
ks, ar = GetKSAR(get(0x50))
|
||||||
|
amsen, d1r = GetAmsenD1R(get(0x60))
|
||||||
|
dt2, d2r = GetDT2D2R(get(0x70))
|
||||||
|
d1l, rr = GetD1LRR(get(0x80))
|
||||||
|
|
||||||
b = get(0x50)
|
PrintOp(op, ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen)
|
||||||
ks = (b & 0b11000000) >> 6
|
|
||||||
ar = (b & 0b00011111)
|
|
||||||
|
|
||||||
b = get(0x60)
|
def PrintOPN(data):
|
||||||
amsen = (b & 0b10000000) >> 7
|
PrintOPNBasic(data)
|
||||||
d1r = (b & 0b00011111)
|
|
||||||
|
|
||||||
b = get(0x70)
|
def PrintOPNA(data):
|
||||||
dt2 = (b & 0b11000000) >> 6
|
PrintOPNBasic(data)
|
||||||
d2r = (b & 0b00011111)
|
PrintOPNBasic(data, True)
|
||||||
|
|
||||||
b = get(0x80)
|
def PrintRaw(data):
|
||||||
d1l = (b & 0b11110000) >> 4
|
slot = 120
|
||||||
rr = (b & 0b00001111)
|
pan = 64
|
||||||
|
ne = 0
|
||||||
|
for n in range(0, len(data) // 32):
|
||||||
|
ofs = n * 32
|
||||||
|
print("@:%d Ins%d" % (n, n))
|
||||||
|
print("LFO: 0 0 0 0 0") # no way to get this data... at least for now
|
||||||
|
|
||||||
print(OpNames[op] + ":", ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen)
|
fl, con = GetFlCon(data[ofs+0x18])
|
||||||
|
ams, pms = GetAMSPMS(data[ofs+0x19])
|
||||||
|
|
||||||
|
PrintChannel(pan, fl, con, ams, pms, slot, ne)
|
||||||
|
|
||||||
|
for op in range(4):
|
||||||
|
get = lambda x: data[ofs + x + OpIdx[op]]
|
||||||
|
dt1, mul = GetDT1Mul(get(0x00))
|
||||||
|
tl = get(0x04)
|
||||||
|
ks, ar = GetKSAR(get(0x08))
|
||||||
|
amsen, d1r = GetAmsenD1R(get(0x0C))
|
||||||
|
dt2, d2r = GetDT2D2R(get(0x10))
|
||||||
|
d1l, rr = GetD1LRR(get(0x14))
|
||||||
|
|
||||||
|
PrintOp(op, ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen)
|
||||||
|
|
||||||
TypeFuncs = {
|
TypeFuncs = {
|
||||||
"opn": PrintOPN,
|
"opn": PrintOPN,
|
||||||
"opna": PrintOPN, # fm portion of opn and opna are functionally equivalent
|
"opna": PrintOPNA,
|
||||||
"opm": PrintOPM
|
"opm": PrintOPM,
|
||||||
|
"raw": PrintRaw
|
||||||
}
|
}
|
||||||
|
|
||||||
Type = GetOption("-t").lower()
|
Type = GetOption("-t")
|
||||||
if Type==None:
|
if Type==None:
|
||||||
Error("no chip type specified")
|
Error("no chip type specified")
|
||||||
if Type not in TypeFuncs:
|
if Type not in TypeFuncs:
|
||||||
@ -150,4 +177,4 @@ if Type not in TypeFuncs:
|
|||||||
|
|
||||||
print("// Exported by hootvopm : https://gitlab.com/whutt/hootvopm")
|
print("// Exported by hootvopm : https://gitlab.com/whutt/hootvopm")
|
||||||
print("// Chip type:",Type)
|
print("// Chip type:",Type)
|
||||||
TypeFuncs[Type](Data)
|
TypeFuncs[Type.lower()](Data)
|
||||||
|
Loading…
Reference in New Issue
Block a user