# -*- coding: utf-8 -*- from direct.directnotify.DirectNotify import DirectNotify import libaudioverse from io import BytesIO import zipfile import os import struct import wave import time as t from hashlib import md5 from Crypto.Cipher import AES from thread import start_new_thread, allocate_lock import sys, traceback def derive_key_and_iv(password, salt, key_length, iv_length): d = d_i = '' while len(d) < key_length + iv_length: d_i = md5(d_i + password + salt).digest() d += d_i return d[:key_length], d[key_length:key_length+iv_length] def encrypt(in_file, out_file, password, key_length=32): bs = AES.block_size salt = Random.new().read(bs - len('Salted__')) key, iv = derive_key_and_iv(password, salt, key_length, bs) cipher = AES.new(key, AES.MODE_CBC, iv) out_file.write('Salted__' + salt) finished = False while not finished: chunk = in_file.read(1024 * bs) if len(chunk) == 0 or len(chunk) % bs != 0: padding_length = bs - (len(chunk) % bs) chunk += padding_length * chr(padding_length) finished = True out_file.write(cipher.encrypt(chunk)) def decrypt(in_file, out_file, password, key_length=32): bs = AES.block_size salt = in_file.read(bs)[len('Salted__'):] key, iv = derive_key_and_iv(password, salt, key_length, bs) cipher = AES.new(key, AES.MODE_CBC, iv) next_chunk = '' finished = False while not finished: chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs)) if len(next_chunk) == 0: padding_length = ord(chunk[-1]) if padding_length < 1 or padding_length > bs: raise ValueError("bad decrypt pad (%d)" % padding_length) # all the pad-bytes must be the same if chunk[-padding_length:] != (padding_length * chr(padding_length)): # this is similar to the bad decrypt:evp_enc.c from openssl program raise ValueError("bad decrypt") chunk = chunk[:-padding_length] finished = True out_file.write(chunk) # return {name: zip.read(name) for name in zip.namelist()} class sound(object): """Dies ist die sound-Klasse. Sie läd und verwaltet Musik und Sounds. :param soundarchiv: der absulute oder relative Pfad zum verschlüsselten Soundarchiv :type soundarchiv: str :param password: Das Passwort, zum entschlüsseln des Archives :type password: str """ def __init__(self, soundarchiv, password): libaudioverse.initialize() self.server = libaudioverse.Server() self.server.set_output_device() self._files = {} self.num_threads = 0 self.thread_started = False self.thread_lock = allocate_lock() t1 = t.clock() self.sounddict = {} with open(soundarchiv, 'rb') as in_file, BytesIO() as out_file: decrypt(in_file, out_file, password) self.zipobj = zipfile.ZipFile(out_file) self.namelist = self.zipobj.namelist() self._names = self.get_files(self.namelist) self.load_all_sounds() print "zeit:", t.clock()-t1, " Sekunden" def get_files_from_archive(self, list, zipfile=None): self.thread_lock.acquire() self.num_threads+=1 self.thread_started = True self.server.threads+=1 self.thread_lock.release() if (zipfile == None): zipfile = self.zipobj self.thread_lock.acquire() for i in list: try: #content=zipfile.read(i) #self._files[list[i]]=content self.sounddict[i] = libaudioverse.Buffer(self.server) #self.sounddict[i].decode_from_array(self._files[i]) self.sounddict[i].decode_from_array(zipfile.read(i)) except: print "fehler", i traceback.print_exc(file=sys.stdout) self.thread_lock.release() #self.thread_lock.acquire() #self._files[list[i]]=content #self.thread_lock.release() self.thread_lock.acquire() self.num_threads-=1 self.thread_started = False self.server.threads-=1 self.thread_lock.release() def load_all_sounds(self): filelist = self.split_list(self._names, 1) for i in filelist: start_new_thread(self.get_files_from_archive, (i,self.zipobj,)) while not self.thread_started: pass while self.num_threads > 0: pass print "files geladen... Buffer werden geladen..." """ for i in filelist: start_new_thread(self.load_buffer, (i,)) while not self.thread_started: pass while self.num_threads > 0: pass """ print "fertig, ", len(self._names), " sounds geladen, mit ", len(filelist), " threads" def split_list(self, l, n): """Splitted eine Liste in gleichgroße Teile wichtig, für das laden mit threads :param l: Liste, die aufgeteilt werden soll :type l: list :param n: die Zahl der Listeneinträge, in den einzelnen Listen :type n: int """ return [l[i:i + n] for i in xrange(0, len(l), n)] def get_mono_sounds(self, l): """Liefert eine Liste von Mono sounds zurück :param l: die gesamte soundliste :type l: list """ return [x for x in l if not x.endswith('/') and not "/music/" in x and not "/ambiance/" in x and not "/voices/" in x] def get_sterio_sounds(self, l): return [x for x in l if not x.endswith('/') and "/music/" in x and "/ambiance/" in x and "/voices/" in x] def get_files(self, l): """Filtert die sounddateien aus der Liste (l)""" return [x for x in l if x.endswith('.wav') and not "sounds/music/loading.wav" in x and not "sounds/pets/loading.wav" in x] def load_buffer(self, filenames): self.thread_lock.acquire() self.num_threads+=1 self.thread_started = True self.thread_lock.release() for i in filenames: #self.thread_lock.acquire() self.sounddict[i] = libaudioverse.Buffer(self.server) self.thread_lock.acquire() print i self.sounddict[i].decode_from_array(self._files[i]) self.thread_lock.release() self.thread_lock.acquire() self.num_threads-=1 self.thread_started = False self.thread_lock.release() if __name__ == "__main__": with open("sounds.zip", 'rb') as in_file, open("sounds.dat", 'wb') as out_file: encrypt(in_file, out_file, "abc")