import os, sys, re, argparse, shutil
ARCHIVE_README = '''These image files for the client should be located in
$prefix/share/crossfire-client. $prefix is the location given in the -prefix
option when configure is run to build the client. The default is /usr/local.
In that case these files should be put in /usr/local/share/crossfire-client
The client will print a messgae if it is unable to find the image information
with the location is looked for them.
'''
class ImageParser(object):
def __init__(self, arch_path, data_path, archive):
self.bmaps_paths_path = os.path.join(data_path, 'bmaps.paths')
self.image_info_path = os.path.join(data_path, 'image_info')
self.arch_path = arch_path
self.archive = archive
if not os.path.isdir(os.path.join(arch_path, 'arch')) or not \
os.path.exists(self.bmaps_paths_path) or not \
os.path.exists(self.image_info_path):
print 'Error while checking for arch path and data files.'
print 'Please make sure you are on the right directory, or that your arguments point to the correct directories'
sys.exit(1)
self.set_name_prefix = 'crossfire.'
self.archive_path_name = 'crossfire-images'
self.archive_format = '''{0}.{1} {2} crossfire.{3}@{4}:{5}\n'''
if self.archive:
print 'Will generate appropriate files for image archive'
if os.path.exists(self.archive_path_name):
print '%s already exists - remove if you really want to remake the images' % self.archive_path_name
sys.exit(1)
try:
os.mkdir(self.archive_path_name, 0755)
except OSError, e:
print 'unable to mkdir %s:\n' % self.archive_path_name, e
sys.exit(1)
self.sets = {}
self.setfiles = {}
self.archive_sets = []
def parseImageInfo(self):
''' This method reads image_info in order to get the information on the image sets available'''
try:
image_info = open(self.image_info_path)
except IOError, e:
print "Can't open image_info file:\n", e
sys.exit(1)
for line in image_info:
if not line.strip() or line.strip()[0] == '#':
continue
infos = line.split(':')
setno = infos[0]
setname = infos[1]
# Check if all the fields are actually returned
if len(infos) != 7:
print 'image_info: line is corrupt:\n%s' % line
# The duplicate will replace previous assignments
if setno in self.sets:
print 'Warning: set %s is duplicated in image_info file' % setno
self.sets[setno] = setname
image_info.close()
def generateSets(self):
self.parseImageInfo()
# Assign the file handlers related to the sets
for num in self.sets:
try:
self.setfiles[num] = open(self.set_name_prefix + num, 'wb')
except IOError, e:
print "Can't open %s for write:\n" % (self.set_name_prefix + num), e
sys.exit(1)
try:
bmaps_paths = open(self.bmaps_paths_path)
except IOError, e:
print "Can't open bmaps.paths:\n", e
sys.exit(1)
for line in bmaps_paths:
# Parse and consequently filter only valid lines
parsed_line = re.findall('(\d{5})\s+(\S+).(\w\w\w)', line)
if not parsed_line:
continue
findex, fname, fnumber = parsed_line[0]
# Display some progress information
if int(findex) % 500 == 0:
print findex, fname
for num in self.sets.iterkeys():
filename = '{0}.{1}.{2}.png'.format(fname, self.sets[num], fnumber)
filename = os.path.join(self.arch_path, filename)
# Set 0 should always contain all images since it's the base set
if not os.path.exists(filename):
if num == '0':
print 'Error: Image %s not found for set 0!' % filename
continue
try:
ifile = open(filename, 'rb')
except IOError, e:
print 'Error reading file %s\n' % filename, e
sys.exit(1)
ifile.seek(0, 2)
eof = ifile.tell()
ifile.seek(0, 0)
buffer = ifile.read(eof)
ifile.close()
# Write 'header' information
self.setfiles[num].write('IMAGE {0} {1}.{2}\n'.format(eof, fname, fnumber))
# If we're archiving this, we need to calculate the checksums
if self.archive:
position = self.setfiles[num].tell()
sum = 0
for i in xrange(eof):
if sum & 01:
sum = (sum >> 1) | 0x80000000
else:
sum >>= 1
sum += ord(buffer[i])
sum &= 0xffffffff
self.archive_sets.append(self.archive_format.format(fname.split('/')[-1], fnumber, sum, self.sets[num], position, eof))
# Write the actual image binary data
self.setfiles[num].write(buffer)
bmaps_paths.close()
for setfile in self.setfiles.itervalues():
setfile.close()
# Write required archiving files and perform file-copying / compressing operations
if self.archive:
try:
bmaps_client = open(os.path.join(self.archive_path_name, 'bmaps.client'), 'wb')
bmaps_client.writelines(sorted(self.archive_sets))
bmaps_client.close()
except IOError, e:
print "Can not open %s/bmaps.client:\n" % self.archive_path_name, e
sys.exit(1)
try:
readme = open(os.path.join(self.archive_path_name, 'README'), 'wb')
readme.write(ARCHIVE_README)
readme.close()
except IOError, e:
print "Can not open %s/README:\n" % self.archive_path_name, e
sys.exit(1)
for count in self.sets.iterkeys():
name = self.set_name_prefix + count
shutil.copyfile(name, os.path.join(self.archive_path_name, name))
shutil.copystat(name, os.path.join(self.archive_path_name, name))
# We do not have a win32 replacement for tar in place yet, so just avoid trying
if sys.platform == 'win32':
print 'Archive folder created, to use it you must compact the folder,',
print 'refer to the README inside the folder for more information'
else:
os.chdir(self.archive_path_name)
os.system('tar cf ../%s.tar .' % self.archive_path_name)
os.chdir('..')
os.system('rm -rf %s' % self.archive_path_name)
def main():
parser = argparse.ArgumentParser(description='This script collects image data into crossfire image set files')
parser.add_argument('-archive', action="store_true", help='Archives the images into a tar file')
parser.add_argument('-src', metavar='ARCH_DIR', default='.', help='Specify a source arch directory (default: ".")')
parser.add_argument('-data', metavar='DATA_DIR', default='.', help='Specify a source data directory (default: ".")')
args = parser.parse_args()
im = ImageParser(args.src, args.data, args.archive)
im.generateSets()
if __name__ == '__main__':
main()