-
Notifications
You must be signed in to change notification settings - Fork 14
/
ccfarchive.py
executable file
·68 lines (57 loc) · 1.99 KB
/
ccfarchive.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
#!/usr/bin/env python
# Author: Bryan Cain (Plombo)
# Date: December 27, 2010
# Description: Reads Wii CCF archives, which contain Genesis and Master System ROMs.
import struct
import zlib
from cStringIO import StringIO
class CCFArchive(object):
# archive: a file-like object containing the CCF archive, OR the path to a CCF archive
def __init__(self, archive):
if type(archive) == type(''):
self.file = open(archive, 'rb')
else:
self.file = archive
self.files = []
self.readheader()
def readheader(self):
magic, zeroes1, rootnode_offset, numfiles, zeroes2 = struct.unpack('<4s12sII8s', self.file.read(32))
assert magic == 'CCF\0'
assert zeroes1 == 12 * '\0'
assert rootnode_offset == 0x20
assert zeroes2 == 8 * '\0'
for i in range(numfiles):
fd = FileDescriptor(self.file)
self.files.append(fd)
def hasfile(self, path):
for f in self.files:
if f.name == path: return True
return False
def getfile(self, path):
assert self.hasfile(path)
fd = None
for f in self.files:
if f.name == path: fd = f
return self.getfile2(fd)
def getfile2(self, fd):
self.file.seek(fd.data_offset * 32)
string = self.file.read(fd.size)
if fd.compressed:
string = zlib.decompress(string)
assert len(string) == fd.decompressed_size
return StringIO(string)
# returns the requested file, even if the name is cut off inside the archive
def find(self, name):
for fd in self.files:
if name.startswith(fd.name.rstrip()) or fd.name.startswith(name.rstrip()): return self.getfile2(fd)
return None
class FileDescriptor(object):
# f: a file-like object of a CCF file at the position of this file descriptor
def __init__(self, f):
self.name, self.data_offset, self.size, self.decompressed_size = struct.unpack('<20sIII', f.read(32))
self.name = self.name[0:self.name.find('\0')]
self.compressed = (self.size != self.decompressed_size)
if __name__ == '__main__':
import os
arc = CCFArchive(os.getenv('HOME') + '/wii/spinball/data.ccf')
arc.getfile('SonicSpinball_USA.S')