changeset 590:e15672733c93

Switch to Python 3.x instead of 2.7.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 30 Sep 2014 17:14:24 +0200
parents 0768122da817
children 2dfa4aa135d2
files pytouhou/formats/exe.py pytouhou/formats/pbg3.py pytouhou/formats/sht.py pytouhou/game/bullettype.py pytouhou/game/enemy.pyx pytouhou/game/game.pyx pytouhou/game/item.pyx pytouhou/game/player.pyx pytouhou/game/sprite.pyx pytouhou/game/text.pxd pytouhou/game/text.py pytouhou/interfaces/eosd.py pytouhou/lib/sdl.pxd pytouhou/lib/sdl.pyx pytouhou/options.py pytouhou/resource/loader.py pytouhou/ui/music.pyx pytouhou/ui/opengl/backend.pxd pytouhou/ui/opengl/backend.pyx pytouhou/ui/opengl/gamerenderer.pyx pytouhou/ui/opengl/renderer.pyx pytouhou/ui/opengl/shader.pyx pytouhou/ui/opengl/sprite.pyx pytouhou/ui/sdl/gamerenderer.py pytouhou/utils/interpolator.pyx pytouhou/utils/matrix.pyx pytouhou/utils/pe.py pytouhou/vm/__init__.py pytouhou/vm/anmrunner.py pytouhou/vm/common.py pytouhou/vm/eclrunner.py pytouhou/vm/msgrunner.py scripts/anmviewer scripts/pytouhou setup.py
diffstat 35 files changed, 126 insertions(+), 126 deletions(-) [+]
line wrap: on
line diff
--- a/pytouhou/formats/exe.py
+++ b/pytouhou/formats/exe.py
@@ -74,17 +74,17 @@ class SHT(object):
 
         format = Struct('<4f2I')
         data_section = [section for section in pe_file.sections
-                            if section.Name.startswith('.data')][0]
+                            if section.Name.startswith(b'.data')][0]
         text_section = [section for section in pe_file.sections
-                            if section.Name.startswith('.text')][0]
+                            if section.Name.startswith(b'.text')][0]
         data_va = pe_file.image_base + data_section.VirtualAddress
         data_size = data_section.SizeOfRawData
         text_va = pe_file.image_base + text_section.VirtualAddress
         text_size = text_section.SizeOfRawData
 
         # Search the whole data segment for 4 successive character definitions
-        for addr in xrange(data_va, data_va + data_size, 4):
-            for character_id in xrange(4):
+        for addr in range(data_va, data_va + data_size, 4):
+            for character_id in range(4):
                 pe_file.seek_to_va(addr + character_id * 24)
                 (speed1, speed2, speed3, speed4,
                  ptr1, ptr2) = format.unpack(pe_file.file.read(format.size))
@@ -101,7 +101,7 @@ class SHT(object):
                 # Now, make sure the shoot function wrappers pass valid addresses
 
                 # Search for the “push” instruction
-                for i in xrange(20):
+                for i in range(20):
                     # Find the “push” instruction
                     pe_file.seek_to_va(ptr1 + i)
                     instr1, shtptr1 = unpack('<BI', pe_file.file.read(5))
@@ -137,7 +137,7 @@ class SHT(object):
     def read(cls, file):
         pe_file = PEFile(file)
         data_section = [section for section in pe_file.sections
-                            if section.Name.startswith('.data')][0]
+                            if section.Name.startswith(b'.data')][0]
         data_va = pe_file.image_base + data_section.VirtualAddress
         data_size = data_section.SizeOfRawData
 
@@ -148,7 +148,7 @@ class SHT(object):
 
         characters = []
         shots_offsets = {}
-        for character in xrange(4):
+        for character in range(4):
             sht = cls()
 
             pe_file.seek_to_va(character_records_va + 6*4*character)
@@ -169,7 +169,7 @@ class SHT(object):
 
             for sht, func_offset in ((sht, shots_func_offset), (focused_sht, shots_func_offset_focused)):
                 # Search for the “push” instruction
-                for i in xrange(20):
+                for i in range(20):
                     # Find the “push” instruction
                     pe_file.seek_to_va(func_offset + i)
                     instr, offset = unpack('<BI', file.read(5))
@@ -184,12 +184,12 @@ class SHT(object):
                     shots_offsets[offset] = []
                 shots_offsets[offset].append(sht)
 
-        for shots_offset, shts in shots_offsets.iteritems():
+        for shots_offset, shts in shots_offsets.items():
             pe_file.seek_to_va(shots_offset)
 
             level_count = 9
             levels = []
-            for i in xrange(level_count):
+            for i in range(level_count):
                 shots_count, power, offset = unpack('<III', file.read(3*4))
                 levels.append((shots_count, power, offset))
 
@@ -199,7 +199,7 @@ class SHT(object):
                 shots[power] = []
                 pe_file.seek_to_va(offset)
 
-                for i in xrange(shots_count):
+                for i in range(shots_count):
                     shot = Shot()
 
                     data = unpack('<HH6fHBBhh', file.read(36))
--- a/pytouhou/formats/pbg3.py
+++ b/pytouhou/formats/pbg3.py
@@ -118,7 +118,7 @@ class PBG3(object):
             checksum = bitstream.read_int() # Checksum of *compressed data*
             offset = bitstream.read_int()
             size = bitstream.read_int()
-            name = bitstream.read_string(255).decode('ascii')
+            name = bitstream.read_string(255)
             entries[name] = PBG3Entry(unknown1, unknown2, checksum, offset, size)
 
         return PBG3(entries, bitstream)
@@ -148,7 +148,7 @@ class PBG3(object):
             self.bitstream.seek(offset)
             value = 0
             for c in self.bitstream.io.read(compressed_size):
-                value += ord(c)
+                value += c
                 value &= 0xFFFFFFFF
             if value != checksum:
                 logger.warn('corrupted data!')
--- a/pytouhou/formats/sht.py
+++ b/pytouhou/formats/sht.py
@@ -68,7 +68,7 @@ class SHT(object):
          sht.diagonal_speed, sht.diagonal_focused_speed) = data
 
         levels = []
-        for i in xrange(level_count):
+        for i in range(level_count):
             offset, power = unpack('<II', file.read(8))
             levels.append((power, offset))
 
--- a/pytouhou/game/bullettype.py
+++ b/pytouhou/game/bullettype.py
@@ -14,6 +14,6 @@ class BulletType(object):
         self.launch_anim8_index = launch_anim8_index
         self.hitbox_size = hitbox_size
         assert 3 == len(launch_anim_penalties)
-        for i in xrange(3):
+        for i in range(3):
             self.launch_anim_penalties[i] = launch_anim_penalties[i]
         self.launch_anim_offsets = launch_anim_offsets
--- a/pytouhou/game/enemy.pyx
+++ b/pytouhou/game/enemy.pyx
@@ -196,12 +196,12 @@ cdef class Enemy(Element):
         bullets = self._game.bullets
         nb_bullets_max = self._game.nb_bullets_max
 
-        for shot_nb in xrange(number_of_shots):
+        for shot_nb in range(number_of_shots):
             shot_speed = speed if shot_nb == 0 else speed + (speed2 - speed) * float(shot_nb) / float(number_of_shots)
             bullet_angle = launch_angle
             if type_ in (69, 70, 71, 74):
                 launch_angle += angle
-            for bullet_nb in xrange(bullets_per_shot):
+            for bullet_nb in range(bullets_per_shot):
                 if nb_bullets_max is not None and len(bullets) == nb_bullets_max:
                     break
 
@@ -272,7 +272,7 @@ cdef class Enemy(Element):
             if self._game.stage in [1, 2, 7]:
                 color = 3
         color += 9
-        for i in xrange(number):
+        for i in range(number):
             self._game.new_particle((self.x, self.y), color, 256) #TODO: find the real size.
 
 
--- a/pytouhou/game/game.pyx
+++ b/pytouhou/game/game.pyx
@@ -190,7 +190,7 @@ cdef class Game:
         score = 0
         bonus = 2000
         for bullet in self.bullets:
-            self.new_label((bullet.x, bullet.y), str(bonus))
+            self.new_label((bullet.x, bullet.y), str(bonus).encode())
             score += bonus
             bonus += 10
         self.bullets = []
@@ -216,13 +216,13 @@ cdef class Game:
 
     cpdef new_effect(self, pos, long anim, anm=None, long number=1):
         number = min(number, self.nb_bullets_max - len(self.effects))
-        for i in xrange(number):
+        for i in range(number):
             self.effects.append(Effect(pos, anim, anm or self.etama[1]))
 
 
     cpdef new_particle(self, pos, long anim, long amp, long number=1, bint reverse=False, long duration=24):
         number = min(number, self.nb_bullets_max - len(self.effects))
-        for i in xrange(number):
+        for i in range(number):
             self.effects.append(Particle(pos, anim, self.etama[1], amp, self, reverse=reverse, duration=duration))
 
 
@@ -295,7 +295,7 @@ cdef class Game:
         self.update_background() #TODO: Pri unknown
         if self.msg_runner is not None:
             self.update_msg(keystates[0]) # Pri ?
-            for i in xrange(len(keystates)):
+            for i in range(len(keystates)):
                 keystates[i] &= ~3 # Remove the ability to attack (keystates 1 and 2).
         self.update_players(keystates) # Pri 7
         self.update_enemies() # Pri 9
@@ -308,7 +308,7 @@ cdef class Game:
             self.update_hints() # Not from this game, so unknown.
         for label in self.labels: #TODO: what priority is it?
             label.update()
-        for text in self.texts.itervalues(): #TODO: what priority is it?
+        for text in self.texts.values(): #TODO: what priority is it?
             if text is not None:
                 text.update()
         self.update_faces() # Pri XXX
@@ -580,10 +580,7 @@ cdef class Game:
 
         self.effects = filter_removed(self.effects)
         self.labels = filter_removed(self.labels)
-
-        for key, text in self.texts.items():
-            if text.removed:
-                del self.texts[key]
+        self.texts = {key: text for key, text in self.texts.items() if not text.removed}
 
         # Disable boss mode if it is dead/it has timeout
         if self.boss and self.boss.removed:
--- a/pytouhou/game/item.pyx
+++ b/pytouhou/game/item.pyx
@@ -157,7 +157,7 @@ cdef class Item(Element):
         if score > 0:
             player.score += score
             if label is None:
-                label = self._game.new_label((self.x, self.y), str(score))
+                label = self._game.new_label((self.x, self.y), str(score).encode())
                 if color != 'white':
                     label.set_color(color)
 
--- a/pytouhou/game/player.pyx
+++ b/pytouhou/game/player.pyx
@@ -83,7 +83,7 @@ cdef class Player(Element):
             self._game.new_effect((self.x, self.y), 17)
             self._game.modify_difficulty(-1600)
             self.play_sound('pldead00')
-            for i in xrange(16):
+            for i in range(16):
                 self._game.new_particle((self.x, self.y), 11, 256) #TODO: find the real size and range.
 
 
@@ -205,11 +205,11 @@ cdef class Player(Element):
 
                 m = self.invulnerable_time % 8
                 if m == 7 or self.invulnerable_time == 0:
-                    for i in xrange(3):
+                    for i in range(3):
                         self.sprite._color[i] = 255
                     self.sprite.changed = True
                 elif m == 1:
-                    for i in xrange(3):
+                    for i in range(3):
                         self.sprite._color[i] = 64
                     self.sprite.changed = True
 
@@ -255,7 +255,7 @@ cdef class Player(Element):
                         self.continues -= 1
                     self.continues_used += 1
 
-                    for i in xrange(5):
+                    for i in range(5):
                         self._game.drop_bonus(self.x, self.y, 4, player=self,
                                               end_pos=(self._game.prng.rand_double() * 288 + 48,
                                                        self._game.prng.rand_double() * 192 - 64))
@@ -271,7 +271,7 @@ cdef class Player(Element):
                     self._game.drop_bonus(self.x, self.y, 2, player=self,
                                           end_pos=(self._game.prng.rand_double() * 288 + 48, # 102h.exe@0x41f3dc
                                                    self._game.prng.rand_double() * 192 - 64))        # @0x41f3
-                    for i in xrange(5):
+                    for i in range(5):
                         self._game.drop_bonus(self.x, self.y, 0, player=self,
                                               end_pos=(self._game.prng.rand_double() * 288 + 48,
                                                        self._game.prng.rand_double() * 192 - 64))
--- a/pytouhou/game/sprite.pyx
+++ b/pytouhou/game/sprite.pyx
@@ -57,7 +57,7 @@ cdef class Sprite:
 
         # Cython treats unsigned char* variables as bytes, so we can’t use
         # slicing here.
-        for i in xrange(4):
+        for i in range(4):
             self._color[i] = 255
 
 
--- a/pytouhou/game/text.pxd
+++ b/pytouhou/game/text.pxd
@@ -35,7 +35,7 @@ cdef class Text(GlyphCollection):
     cdef Interpolator fade_interpolator
     cdef unsigned char alpha
 
-    cpdef set_text(self, bytes text)
+    cpdef set_text(self, text)
     #def timeout_update(self)
     #def move_timeout_update(self)
     #def fadeout_timeout_update(self)
@@ -60,7 +60,7 @@ cdef class NativeText(Element):
     cdef public object update
 
     cdef unicode text
-    cdef bytes align #TODO: use a proper enum.
+    cdef str align #TODO: use a proper enum.
     cdef unsigned long frame, timeout, duration, start
     cdef double to[2]
     cdef double end[2]
--- a/pytouhou/game/text.py
+++ b/pytouhou/game/text.py
@@ -98,7 +98,7 @@ class GlyphCollection(Widget):
 
 
 class Text(GlyphCollection):
-    def __init__(self, pos, ascii_anm, back_anm=None, text='',
+    def __init__(self, pos, ascii_anm, back_anm=None, text=b'',
                  xspacing=14, shift=21, back_script=22, align='left'):
         GlyphCollection.__init__(self, pos, ascii_anm, back_anm,
                                  xspacing=xspacing, back_script=back_script)
@@ -119,7 +119,10 @@ class Text(GlyphCollection):
         if text == self.text:
             return
 
-        self.set_sprites([ord(c) - self.shift for c in text])
+        if isinstance(text, str):
+            text = text.encode()
+
+        self.set_sprites([c - self.shift for c in text])
         self.text = text
         self.changed = True
 
--- a/pytouhou/interfaces/eosd.py
+++ b/pytouhou/interfaces/eosd.py
@@ -40,13 +40,13 @@ class EoSDInterface(object):
         self.level_start = []
 
         self.labels = {
-            'highscore': Text((500, 58), self.ascii_anm, front, text='0'),
-            'score': Text((500, 82), self.ascii_anm, front, text='0'),
+            'highscore': Text((500, 58), self.ascii_anm, front, text=b'0'),
+            'score': Text((500, 82), self.ascii_anm, front, text=b'0'),
             'player': Counter((500, 122), front, front, script=16, value=0),
             'bombs': Counter((500, 146), front, front, script=17, value=0),
-            'power': Text((500, 186), self.ascii_anm, front, text='0'),
-            'graze': Text((500, 206), self.ascii_anm, front, text='0'),
-            'points': Text((500, 226), self.ascii_anm, front, text='0'),
+            'power': Text((500, 186), self.ascii_anm, front, text=b'0'),
+            'graze': Text((500, 206), self.ascii_anm, front, text=b'0'),
+            'points': Text((500, 226), self.ascii_anm, front, text=b'0'),
             'framerate': Text((512, 464), self.ascii_anm, front),
             'debug?': Text((0, 464), self.ascii_anm, front),
 
@@ -68,13 +68,13 @@ class EoSDInterface(object):
     def start_stage(self, game, stage):
         self.game = game
         if stage < 6:
-            text = 'STAGE %d' % stage
+            text = ('STAGE %d' % stage).encode()
         elif stage == 6:
-            text = 'FINAL STAGE'
+            text = b'FINAL STAGE'
         elif stage == 7:
-            text = 'EXTRA STAGE'
+            text = b'EXTRA STAGE'
 
-        self.stage_name = NativeText((192, 200), unicode(game.std.name), shadow=True, align='center')
+        self.stage_name = NativeText((192, 200), game.std.name, shadow=True, align='center')
         self.stage_name.set_timeout(240, effect='fadeout', duration=60, start=120)
 
         self.set_song_name(game.std.bgms[0][0])
@@ -86,7 +86,7 @@ class EoSDInterface(object):
 
     def set_song_name(self, name):
         #TODO: use the correct animation.
-        self.song_name = NativeText((384, 432), u'♪ ' + name, shadow=True, align='right')
+        self.song_name = NativeText((384, 432), '♪ ' + name, shadow=True, align='right')
         self.song_name.set_timeout(240, effect='fadeout', duration=60, start=120)
 
 
--- a/pytouhou/lib/sdl.pxd
+++ b/pytouhou/lib/sdl.pxd
@@ -125,7 +125,7 @@ cdef void mix_open_audio(int frequency, 
 cdef void mix_allocate_channels(int numchans) except *
 cdef int mix_volume(int channel, float volume) nogil
 cdef int mix_volume_music(float volume) nogil
-cdef Music load_music(const char *filename)
+cdef Music load_music(str filename)
 cdef Chunk load_chunk(file_)
 cdef Uint32 get_ticks() nogil
 cdef void delay(Uint32 ms) nogil
--- a/pytouhou/lib/sdl.pyx
+++ b/pytouhou/lib/sdl.pyx
@@ -90,8 +90,8 @@ class SDL(object):
 
 
 cdef class Window:
-    def __init__(self, const char *title, int x, int y, int w, int h, Uint32 flags):
-        self.window = SDL_CreateWindow(title, x, y, w, h, flags)
+    def __init__(self, str title, int x, int y, int w, int h, Uint32 flags):
+        self.window = SDL_CreateWindow(title.encode(), x, y, w, h, flags)
         if self.window == NULL:
             raise SDLError(SDL_GetError())
 
@@ -205,7 +205,7 @@ cdef class Surface:
         image = self.surface.pixels
         alpha = alpha_surface.surface.pixels
 
-        for i in xrange(nb_pixels):
+        for i in range(nb_pixels):
             # Only use the red value, assume the others are equal.
             image[3+4*i] = alpha[3*i]
 
@@ -236,8 +236,8 @@ cdef class Chunk:
 
 
 cdef class Font:
-    def __init__(self, const char *filename, int ptsize):
-        self.font = TTF_OpenFont(filename, ptsize)
+    def __init__(self, str filename, int ptsize):
+        self.font = TTF_OpenFont(filename.encode(), ptsize)
         if self.font == NULL:
             raise SDLError(SDL_GetError())
 
@@ -335,9 +335,9 @@ cdef int mix_volume_music(float volume) 
     return Mix_VolumeMusic(int(volume * 128))
 
 
-cdef Music load_music(const char *filename):
+cdef Music load_music(str filename):
     music = Music()
-    music.music = Mix_LoadMUS(filename)
+    music.music = Mix_LoadMUS(filename.encode())
     if music.music == NULL:
         raise SDLError(SDL_GetError())
     return music
--- a/pytouhou/options.py
+++ b/pytouhou/options.py
@@ -13,7 +13,7 @@
 ##
 
 import os
-from ConfigParser import RawConfigParser, NoOptionError
+from configparser import RawConfigParser, NoOptionError
 
 from pytouhou.utils.xdg import load_config_paths, save_config_path
 
--- a/pytouhou/resource/loader.py
+++ b/pytouhou/resource/loader.py
@@ -60,7 +60,7 @@ class Directory(object):
 
 
 class ArchiveDescription(object):
-    _formats = {'PBG3': PBG3}
+    _formats = {b'PBG3': PBG3}
 
     def __init__(self, path, format_class, file_list=None):
         self.path = path
--- a/pytouhou/ui/music.pyx
+++ b/pytouhou/ui/music.pyx
@@ -16,7 +16,7 @@
 from os.path import join
 from glob import glob
 from pytouhou.lib import sdl
-from pytouhou.lib cimport sdl
+from pytouhou.lib.sdl cimport load_music, Music, load_chunk, Chunk
 from pytouhou.utils.helpers import get_logger
 from pytouhou.game.music cimport MusicPlayer
 
@@ -37,27 +37,27 @@ cdef class BGMPlayer(MusicPlayer):
                 track = resource_loader.get_track(posname)
             except KeyError:
                 track = None
-                logger.warn(u'Music description “%s” not found, continuing without looping data.', posname)
-            globname = join(resource_loader.game_dir, bgm[1].encode('ascii')).replace('.mid', '.*')
+                logger.warn('Music description “%s” not found, continuing without looping data.', posname)
+            globname = join(resource_loader.game_dir, bgm[1]).replace('.mid', '.*')
             filenames = glob(globname)
             for filename in reversed(filenames):
                 try:
-                    source = sdl.load_music(filename)
+                    source = load_music(filename)
                 except sdl.SDLError as error:
-                    logger.debug(u'Music file “%s” unreadable: %s', filename, error)
+                    logger.debug('Music file “%s” unreadable: %s', filename, error)
                     continue
                 else:
                     if track is not None:
                         source.set_loop_points(track.start / 44100., track.end / 44100.) #TODO: retrieve the sample rate from the actual track.
                     self.bgms.append(source)
-                    logger.debug(u'Music file “%s” opened.', filename)
+                    logger.debug('Music file “%s” opened.', filename)
                     break
             else:
                 self.bgms.append(None)
-                logger.warn(u'No working music file for “%s”, disabling bgm.', globname)
+                logger.warn('No working music file for “%s”, disabling bgm.', globname)
 
     cpdef play(self, index):
-        cdef sdl.Music bgm
+        cdef Music bgm
         bgm = self.bgms[index]
         if bgm is not None:
             bgm.play(-1)
@@ -82,12 +82,12 @@ cdef class SFXPlayer(MusicPlayer):
             self.next_channel += 1
         return self.channels[name]
 
-    cdef sdl.Chunk get_sound(self, name):
-        cdef sdl.Chunk chunk
+    cdef Chunk get_sound(self, name):
+        cdef Chunk chunk
         if name not in self.sounds:
             wave_file = self.loader.get_file(name)
             try:
-                chunk = sdl.load_chunk(wave_file)
+                chunk = load_chunk(wave_file)
             except sdl.SDLError as error:
                 logger.warn(u'Sound “%s” not found: %s', name, error)
                 chunk = None
--- a/pytouhou/ui/opengl/backend.pxd
+++ b/pytouhou/ui/opengl/backend.pxd
@@ -9,4 +9,4 @@ cdef bint use_debug_group
 cdef bint use_vao
 cdef bint use_framebuffer_blit
 cdef bint use_primitive_restart
-cdef str shader_header
+cdef bytes shader_header
--- a/pytouhou/ui/opengl/backend.pyx
+++ b/pytouhou/ui/opengl/backend.pyx
@@ -66,13 +66,13 @@ def discover_features():
         except KeyError:
             assert version >= 33
             glsl_version = version * 10
-        shader_header = '#version %d\n\n' % glsl_version
+        shader_header = ('#version %d\n\n' % glsl_version).encode()
     else:
         # The attribute keyword isn’t supported past GLSL ES 3.0.
         if version >= 30:
             version = 20
         glsl_version = {20: '100', 30: '300 es'}[version]
-        shader_header = '#version %s\n\nprecision highp float;\n\n' % glsl_version
+        shader_header = ('#version %s\n\nprecision highp float;\n\n' % glsl_version).encode()
 
 
 def create_window(title, x, y, width, height, swap_interval):
--- a/pytouhou/ui/opengl/gamerenderer.pyx
+++ b/pytouhou/ui/opengl/gamerenderer.pyx
@@ -229,7 +229,7 @@ cdef class GameRenderer(Renderer):
 
         black = Color(0, 0, 0, 255)
 
-        for label in texts.itervalues():
+        for label in texts.values():
             texture = (<Texture>label.texture).texture
             rect = Rect(label.x, label.y, label.width, label.height)
             gradient = [Color(*color, a=label.alpha) for color in label.gradient]
--- a/pytouhou/ui/opengl/renderer.pyx
+++ b/pytouhou/ui/opengl/renderer.pyx
@@ -44,7 +44,7 @@ cdef class Texture:
         self.texture = texture
 
         # Find an unused key in the textures array.
-        for key in xrange(MAX_TEXTURES):
+        for key in range(MAX_TEXTURES):
             if renderer.textures[key] == 0:
                 break
         else:
@@ -53,7 +53,7 @@ cdef class Texture:
         self.key = key
         self.pointer = &renderer.textures[key]
         self.pointer[0] = texture
-        for i in xrange(2):
+        for i in range(2):
             renderer.indices[key][i] = self.indices[i]
 
         #XXX: keep a reference so that when __dealloc__ is called self.pointer is still valid.
@@ -141,7 +141,7 @@ cdef class Renderer:
         nb_vertices = 0
         memset(self.last_indices, 0, sizeof(self.last_indices))
 
-        for element_idx in xrange(nb_elements):
+        for element_idx in range(nb_elements):
             element = <object>self.elements[element_idx]
             ox, oy = <short>element.x, <short>element.y
             data = get_sprite_rendering_data(element.sprite)
@@ -201,7 +201,7 @@ cdef class Renderer:
         previous_blendfunc = -1
         previous_texture = -1
 
-        for key in xrange(2 * MAX_TEXTURES):
+        for key in range(2 * MAX_TEXTURES):
             nb_indices = self.last_indices[key]
             if not nb_indices:
                 continue
--- a/pytouhou/ui/opengl/shader.pyx
+++ b/pytouhou/ui/opengl/shader.pyx
@@ -42,9 +42,9 @@ cdef class Shader:
         self.location_cache = {}
 
         # create the vertex shader
-        self.create_shader(vert[0], GL_VERTEX_SHADER)
+        self.create_shader(vert[0].encode(), GL_VERTEX_SHADER)
         # create the fragment shader
-        self.create_shader(frag[0], GL_FRAGMENT_SHADER)
+        self.create_shader(frag[0].encode(), GL_FRAGMENT_SHADER)
 
         #TODO: put those elsewhere.
         glBindAttribLocation(self.handle, 0, 'in_position')
@@ -116,6 +116,8 @@ cdef class Shader:
             self.linked = True
 
     cdef GLint get_uniform_location(self, name):
+        if isinstance(name, str):
+            name = name.encode()
         if name not in self.location_cache:
             loc = glGetUniformLocation(self.handle, name)
             if loc == -1:
--- a/pytouhou/ui/opengl/sprite.pyx
+++ b/pytouhou/ui/opengl/sprite.pyx
@@ -79,7 +79,7 @@ cdef void render_sprite(Sprite sprite) n
     data.bottom = ty * y_1 + toy
     data.top = (ty + th) * y_1 + toy
 
-    for i in xrange(4):
+    for i in range(4):
         data.color[i] = sprite._color[i]
 
     data.key = ((<Texture>sprite.anm.texture).key << 1) | sprite.blendfunc
--- a/pytouhou/ui/sdl/gamerenderer.py
+++ b/pytouhou/ui/sdl/gamerenderer.py
@@ -142,7 +142,7 @@ class GameRenderer(object):
 
         self.font_manager.load(texts)
 
-        for label in texts.itervalues():
+        for label in texts.values():
             texture = label.texture
 
             source = Rect(0, 0, label.width, label.height)
--- a/pytouhou/utils/interpolator.pyx
+++ b/pytouhou/utils/interpolator.pyx
@@ -22,11 +22,11 @@ cdef class Interpolator:
         self._values = <double*>malloc(self._length * sizeof(double))
         self.start_values = <double*>malloc(self._length * sizeof(double))
         self.end_values = <double*>malloc(self._length * sizeof(double))
-        for i in xrange(self._length):
+        for i in range(self._length):
             self._values[i] = values[i]
             self.start_values[i] = self._values[i]
         if end_values is not None:
-            for i in xrange(self._length):
+            for i in range(self._length):
                 self.end_values[i] = end_values[i]
         self.start_frame = start_frame
         self.end_frame = end_frame
@@ -42,7 +42,7 @@ cdef class Interpolator:
 
     property values:
         def __get__(self):
-            return tuple([self._values[i] for i in xrange(self._length)])
+            return tuple([self._values[i] for i in range(self._length)])
 
 
     def __nonzero__(self):
@@ -50,13 +50,13 @@ cdef class Interpolator:
 
 
     cpdef set_interpolation_start(self, unsigned long frame, tuple values):
-        for i in xrange(self._length):
+        for i in range(self._length):
             self.start_values[i] = values[i]
         self.start_frame = frame
 
 
     cpdef set_interpolation_end(self, unsigned long frame, tuple values):
-        for i in xrange(self._length):
+        for i in range(self._length):
             self.end_values[i] = values[i]
         self.end_frame = frame
 
@@ -66,7 +66,7 @@ cdef class Interpolator:
 
 
     cpdef set_interpolation_end_values(self, tuple values):
-        for i in xrange(self._length):
+        for i in range(self._length):
             self.end_values[i] = values[i]
 
 
@@ -76,7 +76,7 @@ cdef class Interpolator:
         self._frame = frame
         if frame + 1 >= self.end_frame: #XXX: skip the last interpolation step
             # This bug is replicated from the original game
-            for i in xrange(self._length):
+            for i in range(self._length):
                 self._values[i] = self.end_values[i]
                 self.start_values[i] = self.end_values[i]
             self.start_frame = frame
@@ -84,7 +84,7 @@ cdef class Interpolator:
             coeff = float(frame - self.start_frame) / float(self.end_frame - self.start_frame)
             if self._formula is not None:
                 coeff = self._formula(coeff)
-            for i in xrange(self._length):
+            for i in range(self._length):
                 start_value = self.start_values[i]
                 end_value = self.end_values[i]
                 self._values[i] = start_value + coeff * (end_value - start_value)
--- a/pytouhou/utils/matrix.pyx
+++ b/pytouhou/utils/matrix.pyx
@@ -41,10 +41,10 @@ cdef void mul(Matrix *mat1, Matrix *mat2
     d1 = <float*>mat1
     d2 = <float*>mat2
     d3 = <float*>out
-    for i in xrange(4):
-        for j in xrange(4):
+    for i in range(4):
+        for j in range(4):
             d3[4*i+j] = 0
-            for k in xrange(4):
+            for k in range(4):
                 d3[4*i+j] += d1[4*i+k] * d2[4*k+j]
     memcpy(mat1, out, sizeof(Matrix))
     free(out)
@@ -52,7 +52,7 @@ cdef void mul(Matrix *mat1, Matrix *mat2
 
 cdef void flip(Matrix *mat) nogil:
     data = <float*>mat
-    for i in xrange(4):
+    for i in range(4):
         data[i] = -data[i]
 
 
@@ -64,14 +64,14 @@ cdef void scale(Matrix *mat, float x, fl
     coordinate[1] = y
     coordinate[2] = z
 
-    for i in xrange(3):
-        for j in xrange(4):
+    for i in range(3):
+        for j in range(4):
             data[4*i+j] *= coordinate[i]
 
 
 cdef void scale2d(Matrix *mat, float x, float y) nogil:
     data = <float*>mat
-    for i in xrange(4):
+    for i in range(4):
         data[  i] *= x
         data[4+i] *= y
 
@@ -80,11 +80,11 @@ cdef void translate(Matrix *mat, float[3
     cdef float item[3]
 
     data = <float*>mat
-    for i in xrange(3):
+    for i in range(3):
         item[i] = data[12+i] * offset[i]
 
-    for i in xrange(3):
-        for j in xrange(4):
+    for i in range(3):
+        for j in range(4):
             data[4*i+j] += item[i]
 
 
@@ -105,9 +105,9 @@ cdef void rotate_x(Matrix *mat, float an
     data = <float*>mat
     cos_a = cos(angle)
     sin_a = sin(angle)
-    for i in xrange(8):
+    for i in range(8):
         lines[i] = data[i+4]
-    for i in xrange(4):
+    for i in range(4):
         data[4+i] = cos_a * lines[i] - sin_a * lines[4+i]
         data[8+i] = sin_a * lines[i] + cos_a * lines[4+i]
 
@@ -119,10 +119,10 @@ cdef void rotate_y(Matrix *mat, float an
     data = <float*>mat
     cos_a = cos(angle)
     sin_a = sin(angle)
-    for i in xrange(4):
+    for i in range(4):
         lines[i] = data[i]
         lines[i+4] = data[i+8]
-    for i in xrange(4):
+    for i in range(4):
         data[  i] =  cos_a * lines[i] + sin_a * lines[4+i]
         data[8+i] = -sin_a * lines[i] + cos_a * lines[4+i]
 
@@ -134,8 +134,8 @@ cdef void rotate_z(Matrix *mat, float an
     data = <float*>mat
     cos_a = cos(angle)
     sin_a = sin(angle)
-    for i in xrange(8):
+    for i in range(8):
         lines[i] = data[i]
-    for i in xrange(4):
+    for i in range(4):
         data[  i] = cos_a * lines[i] - sin_a * lines[4+i]
         data[4+i] = sin_a * lines[i] + cos_a * lines[4+i]
--- a/pytouhou/utils/pe.py
+++ b/pytouhou/utils/pe.py
@@ -65,7 +65,7 @@ class PEStructs:
         directory_format = Struct('<II')
         directory = []
         partial_header = format.unpack(file.read(format.size))
-        directory = [cls._IMAGE_DATA_DIRECTORY(*directory_format.unpack(file.read(directory_format.size))) for i in xrange(16)]
+        directory = [cls._IMAGE_DATA_DIRECTORY(*directory_format.unpack(file.read(directory_format.size))) for i in range(16)]
         return cls._IMAGE_OPTIONAL_HEADER(*(partial_header + (directory,)))
 
     _IMAGE_SECTION_HEADER = namedtuple('_IMAGE_SECTION_HEADER',
@@ -105,7 +105,7 @@ class PEFile(object):
         self.image_base = pe_optional_header.ImageBase
 
         self.sections = [PEStructs.read_image_section_header(file)
-                            for i in xrange(pe_file_header.NumberOfSections)]
+                            for i in range(pe_file_header.NumberOfSections)]
 
 
     def seek_to_va(self, va):
--- a/pytouhou/vm/__init__.py
+++ b/pytouhou/vm/__init__.py
@@ -1,3 +1,3 @@
-from anmrunner import ANMRunner
-from msgrunner import MSGRunner
-from eclrunner import ECLMainRunner
+from .anmrunner import ANMRunner
+from .msgrunner import MSGRunner
+from .eclrunner import ECLMainRunner
--- a/pytouhou/vm/anmrunner.py
+++ b/pytouhou/vm/anmrunner.py
@@ -21,8 +21,7 @@ from pytouhou.vm.common import MetaRegis
 logger = get_logger(__name__)
 
 
-class ANMRunner(object):
-    __metaclass__ = MetaRegistry
+class ANMRunner(metaclass=MetaRegistry):
     __slots__ = ('_anm', '_sprite', 'running', 'sprite_index_offset', 'script',
                  'instruction_pointer', 'frame', 'waiting', 'handlers',
                  'variables', 'version', 'timeout')
--- a/pytouhou/vm/common.py
+++ b/pytouhou/vm/common.py
@@ -16,9 +16,9 @@
 class MetaRegistry(type):
     def __new__(mcs, name, bases, classdict):
         instruction_handlers = {}
-        for item in classdict.itervalues():
+        for item in classdict.values():
             if hasattr(item, '_instruction_ids'):
-                for version, instruction_ids in item._instruction_ids.iteritems():
+                for version, instruction_ids in item._instruction_ids.items():
                     for id_ in instruction_ids:
                         instruction_handlers.setdefault(version, {})[id_] = item
         classdict['_handlers'] = instruction_handlers
--- a/pytouhou/vm/eclrunner.py
+++ b/pytouhou/vm/eclrunner.py
@@ -23,8 +23,7 @@ logger = get_logger(__name__)
 
 
 
-class ECLMainRunner(object):
-    __metaclass__ = MetaRegistry
+class ECLMainRunner(metaclass=MetaRegistry):
     __slots__ = ('_main', '_subs', '_game', 'frame',
                  'instruction_pointer', 'boss_wait', 'handlers')
 
@@ -118,8 +117,7 @@ class ECLMainRunner(object):
 
 
 
-class ECLRunner(object):
-    __metaclass__ = MetaRegistry
+class ECLRunner(metaclass=MetaRegistry):
     __slots__ = ('_subs', '_enemy', '_game', '_pop_enemy', 'variables', 'sub',
                  'frame', 'instruction_pointer', 'comparison_reg', 'stack',
                  'running', 'handlers')
@@ -934,12 +932,12 @@ class ECLRunner(object):
                 self._game.drop_bonus(self._enemy.x - 64 + self._game.prng.rand_double() * 128,
                                       self._enemy.y - 64 + self._game.prng.rand_double() * 128,
                                       2)
-            for i in xrange(number - 1):
+            for i in range(number - 1):
                 self._game.drop_bonus(self._enemy.x - 64 + self._game.prng.rand_double() * 128,
                                       self._enemy.y - 64 + self._game.prng.rand_double() * 128,
                                       0)
         else:
-            for i in xrange(number):
+            for i in range(number):
                 self._game.drop_bonus(self._enemy.x - 64 + self._game.prng.rand_double() * 128,
                                       self._enemy.y - 64 + self._game.prng.rand_double() * 128,
                                       1)
@@ -986,7 +984,7 @@ class ECLRunner(object):
                 self._game.time_stop = False
         elif function == 7: # Remilia’s laser webs
             base_angle = self._game.prng.rand_double() * 2 * pi
-            for i in xrange(16):
+            for i in range(16):
                 delta = [+pi / 4., -pi / 4.][i % 2]
                 ox, oy = self._enemy.bullet_launch_offset
                 length = 32. #TODO: check
--- a/pytouhou/vm/msgrunner.py
+++ b/pytouhou/vm/msgrunner.py
@@ -21,8 +21,7 @@ from pytouhou.game import NextStage
 logger = get_logger(__name__)
 
 
-class MSGRunner(object):
-    __metaclass__ = MetaRegistry
+class MSGRunner(metaclass=MetaRegistry):
     __slots__ = ('_msg', '_game', 'frame', 'sleep_time', 'allow_skip',
                  'skipping', 'frozen', 'ended', 'instruction_pointer',
                  'handlers')
--- a/scripts/anmviewer
+++ b/scripts/anmviewer
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 # -*- encoding: utf-8 -*-
 ##
 ## Copyright (C) 2011 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
--- a/scripts/pytouhou
+++ b/scripts/pytouhou
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 # -*- encoding: utf-8 -*-
 ##
 ## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
--- a/setup.py
+++ b/setup.py
@@ -76,7 +76,7 @@ default_libs = {
 
 def get_arguments(arg, libraries):
     try:
-        return check_output([COMMAND, arg] + libraries).split()
+        return check_output([COMMAND, arg] + libraries).decode().split()
     except CalledProcessError:
         # The error has already been displayed, just exit.
         sys.exit(1)
@@ -96,6 +96,8 @@ if use_opengl:
 
 
 for directory, _, files in os.walk('pytouhou'):
+    if directory.endswith('/__pycache__'):
+        continue
     package = directory.replace(os.path.sep, '.')
     if not use_opengl and package in ('pytouhou.ui.opengl', 'pytouhou.ui.opengl.shaders'):
         continue