· 8 years ago · Mar 19, 2018, 01:26 AM
1# bigdecoder.py, by aderyn@gmail.com, 2009-03-01
2#
3# decoder for .BIG-format files utilized by Red Alert 3 and C&C: Zero Hours
4# among others. .big is a trivial archival format. quite frankly, this is
5# probably the simplest compound file format imaginable.
6#
7# this script is written for microsoft windows. it can probably be easily
8# adapted for other platforms, but i haven't tried.
9#
10# file structure:
11# the file consists of a global header, an index of all the embedded
12# files, and the actual file data.
13#
14# global header {
15# header, charstring, 4 bytes - always BIG4 or something similiar
16# total file size, unsigned integer, 4 bytes, little endian byte order
17# number of embedded files, unsigned integer, 4 bytes, big endian byte order
18# total size of index table in bytes, unsigned integer, 4 bytes, big endian byte order
19# }, only occurs once
20#
21# index of files, follows directly after
22# the global header:
23# index entry {
24# position of embedded file within BIG-file, unsigned integer, 4 bytes, big endian byte order
25# size of embedded data, unsigned integer, 4 bytes, big endian byte order
26# file name, cstring, ends with null byte
27# }, repeats for each embedded file
28#
29# file data:
30# raw file data at the positions specified in the index
31
32import struct
33import sys
34import os
35import os.path
36
37# Define an empty class to emulate a c struct
38# that can hold the data for each entry in the
39# file index.
40class entry:
41 pass
42
43if len(sys.argv) != 3:
44 print "usage: python bigdecoder.py [file] [target]"
45 exit()
46
47print "BIG-file decoder by aderyn@gmail.com"
48
49filePath = sys.argv[1]
50targetDir = sys.argv[2]
51
52if not os.path.exists(filePath):
53 print "Requested file doesn't exist."
54 exit()
55
56if targetDir[-1] != "\\":
57 targetDir += "\\"
58
59print "Processing " + filePath
60
61# open the file in binary read mode.
62# without the b-flag the tell-method
63# returns the wrong value.
64file = open(filePath, "rb")
65
66# read global header:
67
68# this seems to vary. zero hour uses BIGF
69header = file.read(4)
70if header != "BIG4":
71 print "Invalid file format."
72 exit()
73
74# this seems to be the only value encoded in
75# little-endian order.
76(size,) = struct.unpack("I", file.read(4))
77print "size: %d" % (size,)
78
79(entryCount,indexSize) = struct.unpack(">II", file.read(8))
80print "entry count: %d" % (entryCount,)
81print "index size: %d" % (indexSize,)
82
83print
84
85# read the index table:
86
87# assume that the file contains the amount of
88# entries specified by the global header
89entries = []
90for j in xrange(0, entryCount):
91
92 (entryPos,entrySize) = struct.unpack(">II", file.read(8))
93
94 # the filename is stored as a cstring and
95 # ends with a null byte. read until we reach
96 # this byte.
97 fileName = ""
98 while True:
99 n = file.read(1)
100 if ord(n) == 0:
101 break
102
103 fileName += n
104
105 e = entry()
106 e.name = fileName
107 e.position = entryPos
108 e.size = entrySize
109
110 entries.append(e)
111
112# iterate through the index entries and
113# copy the data into separate files.
114for i, e in enumerate(entries):
115 print "opening %s (size: %d, position: %d)" % (e.name,e.size,e.position)
116 print "file %d of %d" % (i+1, entryCount)
117
118 # calculate the path where the file will be created
119 # in order to ensure that the directories needed actually
120 # exists
121 fileTargetDir = targetDir + e.name[0:e.name.rfind("\\")] + "\\"
122 fileName = e.name[e.name.rfind("\\")+1:]
123 targetPath = fileTargetDir + fileName
124
125 # create the directories if they don't exist.
126 if not os.path.exists(fileTargetDir):
127 os.makedirs(fileTargetDir)
128
129 # skip files that already exist.
130 if os.path.exists(targetPath):
131 print "%s exists. Skipping." % (targetPath,)
132 continue
133
134 print "Opening %s for writing" % (targetPath,)
135 targetFile = open(targetPath, "wb")
136
137 print "Seeked to %d" % (e.position,)
138 file.seek(e.position)
139
140 print "Starting data transfer"
141 for i in xrange(0, e.size):
142 byte = file.read(1)
143 targetFile.write(byte)
144
145 print "Wrote %d bytes" % (e.size,)
146
147 print "Done, closing file."
148 targetFile.close()
149
150 print