/
snesrestore.py
executable file
·135 lines (110 loc) · 3.69 KB
/
snesrestore.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python
# Author: Bryan Cain
# Date: December 31, 2010
# Description: Attempts to restore the original sound samples to SNES ROMs in
# Virtual Console games, which use uncompressed PCM data separate from the ROM.
import os
import sys
import struct
from brrencode3 import BRREncoder
from cStringIO import StringIO
# vcrom: file-like object for the original VC ROM
# brr: file-like object containing the BRR-compressed audio samples
# Precondition: brr is the correct size
def restore_brr_samples(vcrom, pcm):
# read the samples from the input ROM into memory (TODO: check file size first)
vcrom.seek(0)
str = vcrom.read()
samplestart = str.find('PCMF') # samples start with first instance of the string "PCMF"
str = str[samplestart:]
# initialize output ROM in memory as a StringIO (pseudo-file)
rom = StringIO()
rom.write(str)
# read the input BRR samples
#brr.seek(0)
#brrdata = brr.read()
#lastbrroffset = None
enc = BRREncoder(pcm, None)
lastpcmoffset = None
# misc. variables
#goodrom = open('SNADNE1.667', 'rb')
wrong = 0
controlwrong = 0
indices = []
while str.find('PCMF') >= 0:
index = str.find('PCMF')
filepos = samplestart + index
# error checking to prevent infinite loops
#assert index not in indices
#indices.append(index)
pcmf, pcmoffset = struct.unpack('<4sI', str[index:index+8])
pcmoffset &= 0xffffff
if pcmoffset % 16 or pcmoffset < lastpcmoffset:
#print '%08x: unexpected offset %d' % (filepos, pcmoffset)
pcmoffset = lastpcmoffset + 16
#else:
# brroffset = 9 * (brroffset >> 4)
# read the BRR sample
#brr.seek(brroffset)
#brrsample = brr.read(9)
# read and encode the BRR block
brrsample = enc.encode_block(pcmoffset)
# error checking for invalid BRR offsets
if len(brrsample) != 9:
raise ValueError('Invalid BRR offset: %d' % brroffset)
# set the END bit in the BRR sample if it is set in the PCMF block
if ord(str[index+7]) & 1:
brrsample = chr(ord(brrsample[0]) | 1) + brrsample[1:]
# set the LOOP bit in the BRR sample if it is set in the PCMF block
if ord(str[index+7]) & 2:
brrsample = chr(ord(brrsample[0]) | 2) + brrsample[1:]
# checks whether sample matches the original ROM, when the original ROM is available (for debugging purposes)
'''goodrom.seek(samplestart + index)
grsample = goodrom.read(9)
if brrsample != grsample:
wrong += 1
sys.stdout.write('%08x: ' % filepos)
#if brrdata.find(grsample) >= 0:
# print 'wrong sample, correct BRR offset is %08x' % brrdata.find(grsample)
if brrsample[1:] == grsample[1:]:
if abs(ord(brrsample[0]) - ord(grsample[0])) <= 3:
controlwrong += 1
print 'SPC700 control bits differ'
else:
print 'flags are different'
else:
print 'sample encoded differently?' '''
rom.seek(index)
rom.write(brrsample)
str = rom.getvalue()
lastpcmoffset = pcmoffset
#print '%d wrong samples' % wrong
#print '%d differences in SPC700 control bits' % controlwrong
rom.close()
vcrom.seek(0)
return vcrom.read(samplestart) + str
if __name__ == '__main__':
import time
if len(sys.argv) != 4:
print 'Usage: snesrestore game.rom game.pcm output.smc'
sys.exit(1)
vcrom = open(sys.argv[1], 'rb')
pcm = open(sys.argv[2], 'rb')
'''# encode raw PCM in SNES BRR format
print 'Encoding audio as BRR'
brr = StringIO()
enc = BRREncoder(pcm, brr)
enc.encode()
pcm.close()'''
# encode and inject BRR sound data into the ROM
print 'Encoding and restoring BRR audio data to ROM'
start = time.clock()
string = restore_brr_samples(vcrom, pcm)
end = time.clock()
print 'Time: %.2f seconds' % (end - start)
# write to file
output = open(sys.argv[3], "wb")
output.write(string)
output.close()
pcm.close()
#print 'done'