diff --git a/README.md b/README.md index fa9db9d..bbb3580 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,34 @@ This script converts a [Hoot](http://snesmusic.org/hoot/v2/) memory dump into an ## 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. +Pass -b to instead read straight binary data instead of comma-separated values. + ### Currently-supported chip types * OPM * 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 diff --git a/hootvopm.py b/hootvopm.py index 96ec810..d791d6e 100644 --- a/hootvopm.py +++ b/hootvopm.py @@ -8,7 +8,7 @@ def Error(*args): if len(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): global Options @@ -24,22 +24,50 @@ def GetOption(opt): if len(Args) >= 1: Filename = Args[0] else: - Error("need a file, genius") + Error("no input file") - -DataFile = open(Filename,"r") +BinMode = OptionExist("-b") +DataFile = open(Filename,"r" + ("b" if BinMode else "")) if Filename != "-" else sys.stdin # MiOPMdrv sound bank Paramer Ver2002.04.22 # LFO: LFRQ AMD PMD WF NFRQ # @:[Num] [Name] # CH: PAN FL CON AMS PMS SLOT NE # [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"] OpIdx = [0,2,1,3] -for line in DataFile.readlines(): - for byte in line.strip().split(",")[:16]: - Data.append(int(byte,16)) +if not BinMode: + for line in DataFile.readlines(): + 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): ne = (data[0xf] & 0b10000000) >> 7 @@ -52,97 +80,96 @@ def PrintOPM(data): meta = data[0x20 + i : 0x3f + i : 8] - fl = (meta[0] & 0b00111000) >> 3 - con = (meta[0] & 0b00000111) + fl, con = GetFlCon(meta[0]) - pms = (meta[3] & 0b01110000) >> 4 - ams = (meta[3] & 0b00000011) + pms, ams = GetAMSPMS(meta[3]) - print("CH:", pan, fl, con, ams, pms, slot, ne) + PrintChannel(pan, fl, con, ams, pms, slot, ne) for op in range(4): get = lambda x: data[x + i + OpIdx[op]*8] - b = get(0x40) - dt1 = (b & 0b01110000) >> 4 - mul = (b & 0b00001111) - + dt1, mul = GetDT1Mul(get(0x40)) tl = get(0x60) - - b = get(0x80) - 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) + ks, ar = GetKSAR(get(0x80)) + amsen, d1r = GetAmsenD1R(get(0xa0)) + dt2, d2r = GetDT2D2R(get(0xc0)) + d1l, rr = GetD1LRR(get(0xe0)) + + PrintOp(op, ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen) print() -def PrintOPN(data): +def PrintOPNBasic(data, opna = False): slot = 120 pan = 64 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 # likewise, SL -> D1L and DR -> D1R + ofs = 0x100 if opna else 0 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 - fbcon = data[0xb0 + i] - fl = (fbcon & 0b00111000) >> 3 - con = (fbcon & 0b00000111) + fl, con = GetFlCon(data[ofs + 0xb0 + i]) - sens = data[0xb4 + i] - ams = (sens & 0b00110000) >> 4 - pms = (sens & 0b00000111) + ams, pms = GetAMSPMS(data[ofs + 0xb4 + i]) - print("CH:", pan, fl, con, ams, pms, slot, ne) + PrintChannel(pan, fl, con, ams, pms, slot, ne) for op in range(4): - get = lambda x: data[x + i + OpIdx[op]*4] - - b = get(0x30) - dt1 = (b & 0b01110000) >> 4 - mul = (b & 0b00001111) + get = lambda x: data[ofs + x + i + OpIdx[op]*4] + dt1, mul = GetDT1Mul(get(0x30)) 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) - ks = (b & 0b11000000) >> 6 - ar = (b & 0b00011111) - - b = get(0x60) - amsen = (b & 0b10000000) >> 7 - d1r = (b & 0b00011111) - - b = get(0x70) - dt2 = (b & 0b11000000) >> 6 - d2r = (b & 0b00011111) - - b = get(0x80) - d1l = (b & 0b11110000) >> 4 - rr = (b & 0b00001111) - - print(OpNames[op] + ":", ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen) - + PrintOp(op, ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen) + +def PrintOPN(data): + PrintOPNBasic(data) + +def PrintOPNA(data): + PrintOPNBasic(data) + PrintOPNBasic(data, True) + +def PrintRaw(data): + slot = 120 + 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 + + 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 = { "opn": PrintOPN, - "opna": PrintOPN, # fm portion of opn and opna are functionally equivalent - "opm": PrintOPM + "opna": PrintOPNA, + "opm": PrintOPM, + "raw": PrintRaw } -Type = GetOption("-t").lower() +Type = GetOption("-t") if Type==None: Error("no chip type specified") if Type not in TypeFuncs: @@ -150,4 +177,4 @@ if Type not in TypeFuncs: print("// Exported by hootvopm : https://gitlab.com/whutt/hootvopm") print("// Chip type:",Type) -TypeFuncs[Type](Data) +TypeFuncs[Type.lower()](Data)