############################################################################# ## This file is part of the Ostendo Resource Manager. ## ##-------------------------------------------------------------------------## ## Copyright 2007-2008 Bryce Schroeder ## ## bryce.schroeder@gmail.com ## ## http://www.ferazelhosting.net/~bryce/ ## ##-------------------------------------------------------------------------## ## This program is free software: you can redistribute it and/or modify ## ## it under the terms of the GNU General Public License as published by ## ## the Free Software Foundation, either version 3 of the License, or ## ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## ## along with this program. If not, see . ## ############################################################################# ERROR_DOMAIN = "Resource Manager" ERROR_INDENT = 2 import error import os import tarfile import cStringIO import standard_interpreters from standard_interpreters import Resource #import glob e = error.Error_Messenger(ERROR_DOMAIN,ERROR_INDENT) # all of the headers are followed by a parameter line, # which is a python tuple prefixed by a length byte. HEADER_LOAD = "~OSTENDO-01 LOAD\n" # (server name, pack name, number of restypes) HEADER_RESTYPE = "\n\n~OSTENDO-01 RESTYPE\n" # (typename, number of resources of this type) HEADER_RESOURCE = "\n\n~OSTENDO-01 RESOURCE\n" # parameters: (part count, name) HEADER_PART = "\n\n~OSTENDO-01 PART\n" # (part name, part length) class Resource_Type(object): def __init__(self, g, type, interpreter=None): if not interpreter: interpreter = Resource self.interpreter = interpreter # class self.g = g self.resources = {} self.unloaded_resources = {} self.type = type def write_out(self, file): file.write(HEADER_RESTYPE) parameters = "('%s', %d)\n"%(self.type, len(self.resources)) file.write(chr(len(parameters))+parameters) for resource in self.resources.values(): resource.write_out(file) def get(self, name, fail_quietly=False): #print "getting(",name try: res = self.resources[name] except KeyError: res = self.lazy_load_resource(name) if res: return res else: if not fail_quietly: e.err("Couldn't find resource (%s:'%s')."%(self.type,name)) exit(2) return None def lazy_load_resource(self, name): #print "Loading lazily", name try: ures = self.unloaded_resources[name] except IndexError: return None resource = self.interpreter(*ures) self.resources[name] = resource resource.resolve() resource.integrate() return self.resources[name] def add_resource_file(self, file, part_count, resname): files = [] print "\t%s %s"%(resname, part_count) i = 0 while i < part_count: header = file.read(len(HEADER_PART)) if header != HEADER_PART: e.err("Bad header part.") exit(7) length = ord(file.read(1)) parameters = eval(file.read(length)) part_name = parameters[0] part_length = parameters[1] files.append((file.read(part_length),part_name)) i += 1 #if self.resources.has_key(resname): # e.err("Replacing %s:'%s' with resource from %s."%( # self.type,resname,file)) self.unloaded_resources[resname] = (self.g.resources, self.type, resname, files, self.g) #self.resources[resname] = self.interpreter(self.g.resources, # self.type, resname, files, self.g) # add_files expects a list like [(file,part), ] def add_resource_dir(self, directory): files = [] resname = os.path.split(directory)[1] for item in os.listdir(directory): # ignore backup files from editors if item[-1] == '~' or item[0] == '.': continue path = os.path.join(directory,item) data = open(path, 'rb').read() #print "FILE", path, "(%d) bytes"%len(data) files.append((data,item.split(".")[0])) #the interpreter is "Resource" by default if self.resources.has_key(resname): e.err("Replacing %s:'%s' with resource from %s."%( self.type,resname,directory)) self.unloaded_resources[resname] = (self.g.resources, self.type, resname, files) #self.resources[resname] = self.interpreter(self.g.resources, # self.type, resname, files) def resolve(self): for item in self.resources.values(): item.resolve() def integrate(self): for item in self.resources.values(): item.integrate() # (server name, pack name, number of restypes) class Resource_Manager(object): def __init__(self, g): self.types = {} self.g = g def write_out(self, file, name='Local Archive', packname='Default'): file.write(HEADER_LOAD) parameters = "('%s', '%s', %d)\n"%(name, packname, len(self.types)) file.write(chr(len(parameters))+parameters) for type in self.types.values(): type.write_out(file) def __call__(self, restype, name): return self.get(restype, name) def get(self, restype, name, fail_quietly=False): try: return self.types[restype].get(name,fail_quietly) except KeyError: if not fail_quietly: e.err("Couldn't find type ('%s':'%s')."%(restype,name)) exit(3) return None def load(self, path): """Determine if the path is an archive or a directory.""" if os.path.isdir(path): self.load_directory(path) else: self.load_archive(path) def load_archive(self, path): # logic for decompression should go here self.load_from_file(open(path)) def load_from_server(self, socket): self.load_from_file(socket.makefile()) def load_from_file(self, nfile): header = nfile.read(len(HEADER_LOAD)) if header != HEADER_LOAD: e.err("Server gave bad header '%s'. Socket %s."%(header,socket)) exit(4) length = ord(nfile.read(1)) parameters = eval(nfile.read(length)) servername=parameters[0] packname=parameters[1] print "Getting resources from %s: Package %s."%(servername,packname) print "%d resource types."%parameters[2] nrestypes = parameters[2] i = 0 while i < nrestypes: header = nfile.read(len(HEADER_RESTYPE)) if header != HEADER_RESTYPE: e.err("Server gave bad restype header.") exit(5) length = ord(nfile.read(1)) parameters = eval(nfile.read(length)) print parameters[0] # (typename, number of resources) self.add_restype_file(nfile, parameters[0], parameters[1]) i += 1 print "Done." def load_directory(self, dir): """Add an Ostendo Resources directory.""" for item in os.listdir(dir): if item[0] == '.' or item[-1] == '~': continue path = os.path.join(dir,item) if os.path.isdir(path): self.add_restype_dir(path,item) else: if item == "auto.py": #TODO add autoexec python script e.err("Not running auto.py - unimplemented.") elif item == "show.txt": print open(path).read() else: e.err("Unknown loose file '%s'."%item) def add_restype_file(self, file, type, number): if not self.types.has_key(type): if standard_interpreters.CANON.has_key(type): interpreter = standard_interpreters.CANON[type] else: interpreter = Resource self.types[type] = Resource_Type(self.g,type,interpreter) i = 0 while i < number: header = file.read(len(HEADER_RESOURCE)) if header != HEADER_RESOURCE: e.err("Server gave bad resource header.") exit(6) length = ord(file.read(1)) parameters = eval(file.read(length)) # parameters: (part count, name) self.types[type].add_resource_file(file, parameters[0], parameters[1]) i += 1 def add_restype_dir(self, dir, type): if not self.types.has_key(type): if standard_interpreters.CANON.has_key(type): interpreter = standard_interpreters.CANON[type] else: interpreter = Resource self.types[type] = Resource_Type(self.g,type,interpreter) for item in os.listdir(dir): if item[0] == '.' or item[-1] == '~': continue path = os.path.join(dir,item) if os.path.isdir(path): self.types[type].add_resource_dir(path) else: e.err("Loose file in '%s': '%s'"%(type,path)) #def load_archive(self, file): # """Add a (possibly gz'd or bz2'd) archive to the RM.""" # try: # archive = tarfile.open(file) # except IOError: # e.err("Couldn't open '%s'."%file) # except tarfile.TarError: # e.err("Couldn't open archive '%s'."%file) # # e.err("Loading archives isn't supported yet.") def resolve(self): """Resolve all interdependencies betwen resources.""" for type in self.types.values(): type.resolve() self.integrate() def integrate(self): """Handle resources that may have inherritance features.""" for type in self.types.values(): type.integrate()