diff --git a/launcher/game/EasyDialogsWin.py b/launcher/game/EasyDialogsWin.py
index aea17ed..f63c883 100644
--- a/launcher/game/EasyDialogsWin.py
+++ b/launcher/game/EasyDialogsWin.py
@@ -19,8 +19,6 @@ This module uses DLOG resources 260 and on.
 Based upon STDWIN dialogs with the same names and functions.
 """
 
-from __future__ import division
-
 import os
 
 import ctypes
@@ -606,7 +604,7 @@ def AskFileForOpen(
     ofn.lpstrTitle = windowTitle
     ofn.lpstrInitialDir = defaultLocation
 
-    if typeList and filter(None, typeList):
+    if typeList and [_f for _f in typeList if _f]:
         lpstrFilter = ''
         for typeSpec in typeList:
             try:
@@ -672,10 +670,10 @@ def AskFileForOpen(
     ofn.lpfnHook = LPOFNHOOKPROC(hookProc)
 
     if fn(ctypes.byref(ofn)):
-        filenames = filter(None, filename.split('\0'))
+        filenames = [_f for _f in filename.split('\0') if _f]
         if len(filenames) > 1:
             dir, filenames = filenames[0], filenames[1:]
-            return map(lambda fn: os.path.join(dir, fn), filenames)
+            return [os.path.join(dir, fn) for fn in filenames]
         elif multiple:
             return filenames
         else:
@@ -771,7 +769,7 @@ def AskFolder(
     def BrowseCallback(hwnd, uMsg, lParam, lpData):
         if uMsg == BFFM_INITIALIZED:
             if actionButtonLabel:
-                label = unicode(actionButtonLabel, errors='replace')
+                label = str(actionButtonLabel, errors='replace')
                 user32.SendMessageW(hwnd, BFFM_SETOKTEXT, 0, label)
             if cancelButtonLabel:
                 cancelButton = user32.GetDlgItem(hwnd, IDCANCEL)
@@ -995,14 +993,14 @@ def GetArgv(optionlist=None, commandlist=None, addoldfile=1, addnewfile=1, addfo
             if item[0] == '"':
                 while item[-1] != '"':
                     if not tmplist:
-                        raise RuntimeError, "Unterminated quoted argument"
+                        raise RuntimeError("Unterminated quoted argument")
                     item = item + ' ' + tmplist[0]
                     del tmplist[0]
                 item = item[1:-1]
             if item[0] == "'":
                 while item[-1] != "'":
                     if not tmplist:
-                        raise RuntimeError, "Unterminated quoted argument"
+                        raise RuntimeError("Unterminated quoted argument")
                     item = item + ' ' + tmplist[0]
                     del tmplist[0]
                 item = item[1:-1]
@@ -1029,7 +1027,7 @@ def test():
     argv = GetArgv(optionlist=optionlist, commandlist=commandlist, addoldfile=0)
     Message("Command line: %s"%' '.join(argv))
     for i in range(len(argv)):
-        print 'arg[%d] = %r' % (i, argv[i])
+        print('arg[%d] = %r' % (i, argv[i]))
     ok = AskYesNoCancel("Do you want to proceed?")
     ok = AskYesNoCancel("Do you want to identify?", yes="Identify", no="No")
     if ok > 0:
@@ -1053,11 +1051,11 @@ def test():
     try:
         if hasattr(MacOS, 'SchedParams'):
             appsw = MacOS.SchedParams(1, 0)
-        for i in xrange(20):
+        for i in range(20):
             bar.inc()
             time.sleep(0.05)
         bar.set(0,100)
-        for i in xrange(100):
+        for i in range(100):
             bar.set(i)
             time.sleep(0.05)
             if i % 10 == 0:
diff --git a/launcher/game/change_icon.py b/launcher/game/change_icon.py
index 900cff6..23caaab 100644
--- a/launcher/game/change_icon.py
+++ b/launcher/game/change_icon.py
@@ -62,9 +62,9 @@ class BinFile(object):
     def name(self):
         c = self.u16()
 
-        rv = u""
+        rv = ""
         for _i in range(c):
-            rv += unichr(self.u16())
+            rv += chr(self.u16())
 
         return rv
 
@@ -143,11 +143,11 @@ def parse_directory(bf, offset):
 def show_resources(d, prefix):
 
     if not isinstance(d, dict):
-        print prefix, "Codepage", d[0], "length", len(d[1])
+        print(prefix, "Codepage", d[0], "length", len(d[1]))
         return
 
     for k in d:
-        print prefix, k
+        print(prefix, k)
         show_resources(d[k], prefix + "  ")
 
 ##############################################################################
@@ -201,8 +201,8 @@ class Packer(object):
         return rv
 
     def pack_dict(self, d, offset):
-        name_entries = sorted((a, b) for a, b in d.iteritems() if isinstance(a, unicode))
-        id_entries = sorted((a, b) for a, b in d.iteritems() if isinstance(a, int))
+        name_entries = sorted((a, b) for a, b in d.items() if isinstance(a, str))
+        id_entries = sorted((a, b) for a, b in d.items() if isinstance(a, int))
 
         rv = struct.pack("<IIHHHH", 0, 0, 4, 0, len(name_entries), len(id_entries))
 
@@ -211,7 +211,7 @@ class Packer(object):
         rest = ""
 
         for (name, value) in name_entries + id_entries:
-            if isinstance(name, unicode):
+            if isinstance(name, str):
                 name = 0x80000000 | self.pack_name(name)
 
             if isinstance(value, dict):
@@ -230,7 +230,7 @@ class Packer(object):
 # This loads in an icon file, and returns a dictionary that is suitable for
 # use in the resources of an exe file.
 def load_icon(fn):
-    f = BinFile(file(fn, "rb").read())
+    f = BinFile(open(fn, "rb").read())
 
     f.seek(0)
     f.u16()
@@ -291,7 +291,7 @@ def change_icons(oldexe, icofn):
     physize = rsrc_section.SizeOfRawData
     virsize = rsrc_section.Misc_VirtualSize
 
-    f = file(oldexe, "rb")
+    f = open(oldexe, "rb")
     f.seek(base)
     data = f.read(physize)
     f.close()
@@ -343,7 +343,7 @@ def change_icons(oldexe, icofn):
 
 if __name__ == "__main__":
 
-    f = file(sys.argv[3], "wb")
+    f = open(sys.argv[3], "wb")
     f.write(change_icons(sys.argv[1], sys.argv[2]))
     f.close()
 
diff --git a/launcher/game/pefile.py b/launcher/game/pefile.py
index 9fd10a5..5e20cd3 100644
--- a/launcher/game/pefile.py
+++ b/launcher/game/pefile.py
@@ -83,8 +83,8 @@ IMAGE_OS2_SIGNATURE_LE          = 0x454C
 IMAGE_VXD_SIGNATURE             = 0x454C
 IMAGE_NT_SIGNATURE              = 0x00004550
 IMAGE_NUMBEROF_DIRECTORY_ENTRIES= 16
-IMAGE_ORDINAL_FLAG              = 0x80000000L
-IMAGE_ORDINAL_FLAG64            = 0x8000000000000000L
+IMAGE_ORDINAL_FLAG              = 0x80000000
+IMAGE_ORDINAL_FLAG64            = 0x8000000000000000
 OPTIONAL_HEADER_MAGIC_PE        = 0x10b
 OPTIONAL_HEADER_MAGIC_PE_PLUS   = 0x20b
 
@@ -167,7 +167,7 @@ section_characteristics = [
     ('IMAGE_SCN_MEM_SHARED',                0x10000000),
     ('IMAGE_SCN_MEM_EXECUTE',               0x20000000),
     ('IMAGE_SCN_MEM_READ',                  0x40000000),
-    ('IMAGE_SCN_MEM_WRITE',                 0x80000000L) ]
+    ('IMAGE_SCN_MEM_WRITE',                 0x80000000) ]
 
 SECTION_CHARACTERISTICS = dict([(e[1], e[0]) for e in
     section_characteristics]+section_characteristics)
@@ -574,7 +574,7 @@ class UnicodeStringWrapperPostProcessor:
 
         try:
             data = self.pe.get_data(self.rva_ptr, 2)
-        except PEFormatError, e:
+        except PEFormatError as e:
             return False
 
         if len(data)<2:
@@ -646,7 +646,7 @@ class Dump:
         The text can be indented with the optional argument 'indent'.
         """
 
-        if isinstance(txt, unicode):
+        if isinstance(txt, str):
             try:
                 txt = str(txt)
             except UnicodeEncodeError:
@@ -768,7 +768,7 @@ class Structure:
             self.__all_zeroes__ = True
 
         self.__unpacked_data_elms__ = struct.unpack(self.__format__, data)
-        for i in xrange(len(self.__unpacked_data_elms__)):
+        for i in range(len(self.__unpacked_data_elms__)):
             for key in self.__keys__[i]:
                 #self.values[key] = self.__unpacked_data_elms__[i]
                 setattr(self, key, self.__unpacked_data_elms__[i])
@@ -778,7 +778,7 @@ class Structure:
 
         new_values = []
 
-        for i in xrange(len(self.__unpacked_data_elms__)):
+        for i in range(len(self.__unpacked_data_elms__)):
 
             for key in self.__keys__[i]:
                 new_val = getattr(self, key)
@@ -814,15 +814,15 @@ class Structure:
             for key in keys:
 
                 val = getattr(self, key)
-                if isinstance(val, int) or isinstance(val, long):
+                if isinstance(val, int):
                     val_str = '0x%-8X' % (val)
                     if key == 'TimeDateStamp' or key == 'dwTimeStamp':
                         try:
                             val_str += ' [%s UTC]' % time.asctime(time.gmtime(val))
-                        except exceptions.ValueError, e:
+                        except exceptions.ValueError as e:
                             val_str += ' [INVALID TIME]'
                 else:
-                    val_str = ''.join(filter(lambda c:c != '\0', str(val)))
+                    val_str = ''.join([c for c in str(val) if c != '\0'])
 
                 dump.append('%-30s %s' % (key+':', val_str))
 
@@ -957,7 +957,7 @@ class DataContainer:
     """Generic data container."""
 
     def __init__(self, **args):
-        for key, value in args.items():
+        for key, value in list(args.items()):
             setattr(self, key, value)
 
 
@@ -1371,7 +1371,7 @@ class PE:
 
         try:
             structure.__unpack__(data)
-        except PEFormatError, err:
+        except PEFormatError as err:
             self.__warnings.append(
                 'Corrupt header "%s" at file offset %d. Exception: %s' % (
                     format[0], file_offset, str(err))  )
@@ -1390,7 +1390,7 @@ class PE:
         """
 
         if fname:
-            fd = file(fname, 'rb')
+            fd = open(fname, 'rb')
             self.__data__ = fd.read()
             fd.close()
         elif data:
@@ -1555,7 +1555,7 @@ class PE:
                 'Normal values are never larger than 0x10, the value is: 0x%x' %
                 self.OPTIONAL_HEADER.NumberOfRvaAndSizes )
 
-        for i in xrange(int(0x7fffffffL & self.OPTIONAL_HEADER.NumberOfRvaAndSizes)):
+        for i in range(int(0x7fffffff & self.OPTIONAL_HEADER.NumberOfRvaAndSizes)):
 
             if len(self.__data__[offset:]) == 0:
                 break
@@ -1668,7 +1668,7 @@ class PE:
         """
 
         for warning in self.__warnings:
-            print '>', warning
+            print('>', warning)
 
 
     def full_load(self):
@@ -1705,7 +1705,7 @@ class PE:
                 for entry in self.FileInfo:
                     if hasattr(entry, 'StringTable'):
                         for st_entry in entry.StringTable:
-                            for key, entry in st_entry.entries.items():
+                            for key, entry in list(st_entry.entries.items()):
 
                                 offsets = st_entry.entries_offsets[key]
                                 lengths = st_entry.entries_lengths[key]
@@ -1738,12 +1738,12 @@ class PE:
                                     file_data[
                                         offsets[1] + len(entry)*2 :
                                         offsets[1] + lengths[1]*2 ] = [
-                                            u'\0' ] * remainder*2
+                                            '\0' ] * remainder*2
 
         new_file_data = ''.join( [ chr(ord(c)) for c in file_data] )
 
         if filename:
-            f = file(filename, 'wb+')
+            f = open(filename, 'wb+')
             f.write(new_file_data)
             f.close()
         else:
@@ -1767,7 +1767,7 @@ class PE:
 
         self.sections = []
 
-        for i in xrange(self.FILE_HEADER.NumberOfSections):
+        for i in range(self.FILE_HEADER.NumberOfSections):
             section = SectionStructure(self.__IMAGE_SECTION_HEADER_format__)
             if not section:
                 break
@@ -1846,7 +1846,7 @@ class PE:
         matching the filter "flag_filter".
         """
 
-        return [(f[0], f[1]) for f in flag_dict.items() if
+        return [(f[0], f[1]) for f in list(flag_dict.items()) if
                 isinstance(f[0], str) and f[0].startswith(flag_filter)]
 
 
@@ -1956,7 +1956,7 @@ class PE:
             rva += bnd_descr.sizeof()
 
             forwarder_refs = []
-            for idx in xrange(bnd_descr.NumberOfModuleForwarderRefs):
+            for idx in range(bnd_descr.NumberOfModuleForwarderRefs):
                 # Both structures IMAGE_BOUND_IMPORT_DESCRIPTOR and
                 # IMAGE_BOUND_FORWARDER_REF have the same size.
                 bnd_frwd_ref = self.__unpack_data__(
@@ -2092,7 +2092,7 @@ class PE:
         data = self.get_data(data_rva, size)
 
         entries = []
-        for idx in xrange(len(data)/2):
+        for idx in range(len(data)/2):
             word = struct.unpack('<H', data[idx*2:(idx+1)*2])[0]
             reloc_type = (word>>12)
             reloc_offset = (word&0x0fff)
@@ -2110,10 +2110,10 @@ class PE:
         dbg_size = Structure(self.__IMAGE_DEBUG_DIRECTORY_format__).sizeof()
 
         debug = []
-        for idx in xrange(size/dbg_size):
+        for idx in range(size/dbg_size):
             try:
                 data = self.get_data(rva+dbg_size*idx, dbg_size)
-            except PEFormatError, e:
+            except PEFormatError as e:
                 self.__warnings.append(
                     'Invalid debug information. Can\'t read ' +
                     'data at RVA: 0x%x' % rva)
@@ -2167,7 +2167,7 @@ class PE:
             # If the RVA is invalid all would blow up. Some EXEs seem to be
             # specially nasty and have an invalid RVA.
             data = self.get_data(rva, Structure(self.__IMAGE_RESOURCE_DIRECTORY_format__).sizeof() )
-        except PEFormatError, e:
+        except PEFormatError as e:
             self.__warnings.append(
                 'Invalid resources directory. Can\'t read ' +
                 'directory data at RVA: 0x%x' % rva)
@@ -2201,7 +2201,7 @@ class PE:
 
         strings_to_postprocess = list()
 
-        for idx in xrange(number_of_entries):
+        for idx in range(number_of_entries):
 
             res = self.parse_resource_entry(rva)
             if res is None:
@@ -2227,7 +2227,7 @@ class PE:
                     entry_name = UnicodeStringWrapperPostProcessor(self, ustr_offset)
                     strings_to_postprocess.append(entry_name)
 
-                except PEFormatError, excp:
+                except PEFormatError as excp:
                     self.__warnings.append(
                         'Error parsing the resources directory, ' +
                         'attempting to read entry name. ' +
@@ -2328,7 +2328,7 @@ class PE:
             # If the RVA is invalid all would blow up. Some EXEs seem to be
             # specially nasty and have an invalid RVA.
             data = self.get_data(rva, Structure(self.__IMAGE_RESOURCE_DATA_ENTRY_format__).sizeof() )
-        except PEFormatError, excp:
+        except PEFormatError as excp:
             self.__warnings.append(
                 'Error parsing a resource directory data entry, ' +
                 'the RVA is invalid: 0x%x' % ( rva ) )
@@ -2353,13 +2353,13 @@ class PE:
             return None
 
         #resource.NameIsString = (resource.Name & 0x80000000L) >> 31
-        resource.NameOffset = resource.Name & 0x7FFFFFFFL
+        resource.NameOffset = resource.Name & 0x7FFFFFFF
 
-        resource.__pad = resource.Name & 0xFFFF0000L
-        resource.Id = resource.Name & 0x0000FFFFL
+        resource.__pad = resource.Name & 0xFFFF0000
+        resource.Id = resource.Name & 0x0000FFFF
 
-        resource.DataIsDirectory = (resource.OffsetToData & 0x80000000L) >> 31
-        resource.OffsetToDirectory = resource.OffsetToData & 0x7FFFFFFFL
+        resource.DataIsDirectory = (resource.OffsetToData & 0x80000000) >> 31
+        resource.OffsetToDirectory = resource.OffsetToData & 0x7FFFFFFF
 
         return resource
 
@@ -2407,7 +2407,7 @@ class PE:
         ustr_offset = version_struct.OffsetToData + versioninfo_struct.sizeof()
         try:
             versioninfo_string = self.get_string_u_at_rva( ustr_offset )
-        except PEFormatError, excp:
+        except PEFormatError as excp:
             self.__warnings.append(
                 'Error parsing the version information, ' +
                 'attempting to read VS_VERSION_INFO string. Can\'t ' +
@@ -2418,7 +2418,7 @@ class PE:
 
         # If the structure does not contain the expected name, it's assumed to be invalid
         #
-        if versioninfo_string != u'VS_VERSION_INFO':
+        if versioninfo_string != 'VS_VERSION_INFO':
 
             self.__warnings.append('Invalid VS_VERSION_INFO block')
             return
@@ -2487,7 +2487,7 @@ class PE:
                 stringfileinfo_offset + versioninfo_struct.sizeof() )
             try:
                 stringfileinfo_string = self.get_string_u_at_rva( ustr_offset )
-            except PEFormatError, excp:
+            except PEFormatError as excp:
                 self.__warnings.append(
                     'Error parsing the version information, ' +
                     'attempting to read StringFileInfo string. Can\'t ' +
@@ -2506,7 +2506,7 @@ class PE:
 
             # Parse a StringFileInfo entry
             #
-            if stringfileinfo_string.startswith(u'StringFileInfo'):
+            if stringfileinfo_string.startswith('StringFileInfo'):
 
                 if stringfileinfo_struct.Type == 1 and stringfileinfo_struct.ValueLength == 0:
 
@@ -2533,7 +2533,7 @@ class PE:
                             stringtable_struct.sizeof() )
                         try:
                             stringtable_string = self.get_string_u_at_rva( ustr_offset )
-                        except PEFormatError, excp:
+                        except PEFormatError as excp:
                             self.__warnings.append(
                                 'Error parsing the version information, ' +
                                 'attempting to read StringTable string. Can\'t ' +
@@ -2568,7 +2568,7 @@ class PE:
                             try:
                                 key = self.get_string_u_at_rva( ustr_offset )
                                 key_offset = self.get_offset_from_rva( ustr_offset )
-                            except PEFormatError, excp:
+                            except PEFormatError as excp:
                                 self.__warnings.append(
                                     'Error parsing the version information, ' +
                                     'attempting to read StringTable Key string. Can\'t ' +
@@ -2584,7 +2584,7 @@ class PE:
                                 value = self.get_string_u_at_rva( ustr_offset,
                                     max_length = string_struct.ValueLength )
                                 value_offset = self.get_offset_from_rva( ustr_offset )
-                            except PEFormatError, excp:
+                            except PEFormatError as excp:
                                 self.__warnings.append(
                                     'Error parsing the version information, ' +
                                     'attempting to read StringTable Value string. ' +
@@ -2629,7 +2629,7 @@ class PE:
 
             # Parse a VarFileInfo entry
             #
-            elif stringfileinfo_string.startswith( u'VarFileInfo' ):
+            elif stringfileinfo_string.startswith( 'VarFileInfo' ):
 
                 varfileinfo_struct = stringfileinfo_struct
                 varfileinfo_struct.name = 'VarFileInfo'
@@ -2659,7 +2659,7 @@ class PE:
                             var_struct.sizeof() )
                         try:
                             var_string = self.get_string_u_at_rva( ustr_offset )
-                        except PEFormatError, excp:
+                        except PEFormatError as excp:
                             self.__warnings.append(
                                 'Error parsing the version information, ' +
                                 'attempting to read VarFileInfo Var string. ' +
@@ -2681,7 +2681,7 @@ class PE:
                                 raw_data[varword_offset+2:varword_offset+4], 0)
                             varword_offset += 4
 
-                            if isinstance(word1, (int, long)) and isinstance(word1, (int, long)):
+                            if isinstance(word1, int) and isinstance(word1, int):
                                 var_struct.entry = {var_string: '0x%04x 0x%04x' % (word1, word2)}
 
                         var_offset = self.dword_align(
@@ -2750,7 +2750,7 @@ class PE:
 
         exports = []
 
-        for i in xrange(export_dir.NumberOfNames):
+        for i in range(export_dir.NumberOfNames):
 
 
             symbol_name = self.get_string_at_rva(
@@ -2787,7 +2787,7 @@ class PE:
 
         ordinals = [exp.ordinal for exp in exports]
 
-        for idx in xrange(export_dir.NumberOfFunctions):
+        for idx in range(export_dir.NumberOfFunctions):
 
             if not idx+export_dir.Base in ordinals:
                 symbol_address = self.get_dword_from_data(
@@ -2828,7 +2828,7 @@ class PE:
                 # If the RVA is invalid all would blow up. Some PEs seem to be
                 # specially nasty and have an invalid RVA.
                 data = self.get_data( rva, Structure(self.__IMAGE_DELAY_IMPORT_DESCRIPTOR_format__).sizeof() )
-            except PEFormatError, e:
+            except PEFormatError as e:
                 self.__warnings.append(
                     'Error parsing the Delay import directory at RVA: 0x%x' % ( rva ) )
                 break
@@ -2850,7 +2850,7 @@ class PE:
                     import_desc.pINT,
                     import_desc.pIAT,
                     None)
-            except PEFormatError, e:
+            except PEFormatError as e:
                 self.__warnings.append(
                     'Error parsing the Delay import directory. ' +
                     'Invalid import data at RVA: 0x%x' % ( rva ) )
@@ -2881,7 +2881,7 @@ class PE:
                 # If the RVA is invalid all would blow up. Some EXEs seem to be
                 # specially nasty and have an invalid RVA.
                 data = self.get_data(rva, Structure(self.__IMAGE_IMPORT_DESCRIPTOR_format__).sizeof() )
-            except PEFormatError, e:
+            except PEFormatError as e:
                 self.__warnings.append(
                     'Error parsing the Import directory at RVA: 0x%x' % ( rva ) )
                 break
@@ -2901,7 +2901,7 @@ class PE:
                     import_desc.OriginalFirstThunk,
                     import_desc.FirstThunk,
                     import_desc.ForwarderChain)
-            except PEFormatError, excp:
+            except PEFormatError as excp:
                 self.__warnings.append(
                     'Error parsing the Import directory. ' +
                     'Invalid Import data at RVA: 0x%x' % ( rva ) )
@@ -2934,7 +2934,7 @@ class PE:
         imported_symbols = []
         imports_section = self.get_section_by_rva(first_thunk)
         if not imports_section:
-            raise PEFormatError, 'Invalid/corrupt imports.'
+            raise PEFormatError('Invalid/corrupt imports.')
 
 
         # Import Lookup Table. Contains ordinals or pointers to strings.
@@ -2961,7 +2961,7 @@ class PE:
             return None
 
 
-        for idx in xrange(len(table)):
+        for idx in range(len(table)):
 
             imp_ord = None
             imp_hint = None
@@ -2989,7 +2989,7 @@ class PE:
                         # Get the Hint
                         imp_hint = self.get_word_from_data(data, 0)
                         imp_name = self.get_string_at_rva(table[idx].AddressOfData+2)
-                    except PEFormatError, e:
+                    except PEFormatError as e:
                         pass
 
             imp_address = first_thunk+self.OPTIONAL_HEADER.ImageBase+idx*4
@@ -3030,7 +3030,7 @@ class PE:
 
             try:
                 data = self.get_data( rva, Structure(format).sizeof() )
-            except PEFormatError, e:
+            except PEFormatError as e:
                 self.__warnings.append(
                     'Error parsing the import table. ' +
                     'Invalid data at RVA: 0x%x' % ( rva ) )
@@ -3135,7 +3135,7 @@ class PE:
                     end = None
                 return self.header[rva:end]
 
-            raise PEFormatError, 'data at RVA can\'t be fetched. Corrupt header?'
+            raise PEFormatError('data at RVA can\'t be fetched. Corrupt header?')
 
         return s.get_data(rva, length)
 
@@ -3158,7 +3158,7 @@ class PE:
         s = self.get_section_by_rva(rva)
         if not s:
 
-            raise PEFormatError, 'data at RVA can\'t be fetched. Corrupt header?'
+            raise PEFormatError('data at RVA can\'t be fetched. Corrupt header?')
 
         return s.get_offset_from_rva(rva)
 
@@ -3205,21 +3205,21 @@ class PE:
             # If the RVA is invalid all would blow up. Some EXEs seem to be
             # specially nasty and have an invalid RVA.
             self.get_data(rva, 2)
-        except PEFormatError, e:
+        except PEFormatError as e:
             return None
 
         #length = struct.unpack('<H', data)[0]
 
-        s = u''
-        for idx in xrange(max_length):
+        s = ''
+        for idx in range(max_length):
             try:
                 uchr = struct.unpack('<H', self.get_data(rva+2*idx, 2))[0]
             except struct.error:
                 break
 
-            if unichr(uchr) == u'\0':
+            if chr(uchr) == '\0':
                 break
-            s += unichr(uchr)
+            s += chr(uchr)
 
         return s
 
@@ -3251,7 +3251,7 @@ class PE:
 
     def print_info(self):
         """Print all the PE header information in a human readable from."""
-        print self.dump_info()
+        print(self.dump_info())
 
 
     def dump_info(self, dump=None):
@@ -3334,7 +3334,7 @@ class PE:
             hasattr(self.OPTIONAL_HEADER, 'DATA_DIRECTORY') ):
 
             dump.add_header('Directories')
-            for idx in xrange(len(self.OPTIONAL_HEADER.DATA_DIRECTORY)):
+            for idx in range(len(self.OPTIONAL_HEADER.DATA_DIRECTORY)):
                 directory = self.OPTIONAL_HEADER.DATA_DIRECTORY[idx]
                 dump.add_lines(directory.dump())
             dump.add_newline()
@@ -3368,7 +3368,7 @@ class PE:
                             [dump.add_line('  '+line) for line in st_entry.dump()]
                             dump.add_line('  LangID: '+st_entry.LangID)
                             dump.add_newline()
-                            for str_entry in st_entry.entries.items():
+                            for str_entry in list(st_entry.entries.items()):
                                 dump.add_line( '    ' +
                                     convert_to_printable(str_entry[0]) + ': ' +
                                     convert_to_printable(str_entry[1]) )
@@ -3380,8 +3380,8 @@ class PE:
                                 [dump.add_line('  '+line) for line in var_entry.dump()]
                                 dump.add_line(
                                     '    ' +
-                                    convert_to_printable(var_entry.entry.keys()[0]) +
-                                    ': ' + var_entry.entry.values()[0])
+                                    convert_to_printable(list(var_entry.entry.keys())[0]) +
+                                    ': ' + list(var_entry.entry.values())[0])
 
                         dump.add_newline()
 
@@ -3930,5 +3930,5 @@ class PE:
 if __name__ == "__main__":
     import sys
     pe = PE(sys.argv[1])
-    print pe
+    print(pe)
 
diff --git a/launcher/game/project.rpy b/launcher/game/project.rpy
index 8ff9ade..524e838 100644
--- a/launcher/game/project.rpy
+++ b/launcher/game/project.rpy
@@ -273,7 +273,7 @@ init python in project:
 
             for f in files:
 
-                data = file(self.unelide_filename(f))
+                data = open(self.unelide_filename(f))
 
                 for l, line in enumerate(data):
                     l += 1
diff --git a/launcher/game/tkaskdir.py b/launcher/game/tkaskdir.py
index 5b64008..a88d089 100644
--- a/launcher/game/tkaskdir.py
+++ b/launcher/game/tkaskdir.py
@@ -31,8 +31,8 @@ try:
     from tkinter import Tk
     from tkinter.filedialog import askdirectory
 except ImportError:
-    from Tkinter import Tk
-    from tkFileDialog import askdirectory
+    from tkinter import Tk
+    from tkinter.filedialog import askdirectory
 
 # Binary mode stdout for python3.
 try:
diff --git a/module/gen/renpy.text.textsupport.c b/module/gen/renpy.text.textsupport.c
index f09ec46..bbe9b3a 100644
--- a/module/gen/renpy.text.textsupport.c
+++ b/module/gen/renpy.text.textsupport.c
@@ -800,7 +800,7 @@ int __pyx_module_is_main_renpy__text__textsupport = 0;
 static PyObject *__pyx_builtin_range;
 static PyObject *__pyx_builtin_Exception;
 static PyObject *__pyx_builtin_ord;
-static PyObject *__pyx_builtin_unichr;
+static PyObject *__pyx_builtin_chr;
 static int __pyx_pf_5renpy_4text_11textsupport_5Glyph___init__(struct __pyx_obj_5renpy_4text_11textsupport_Glyph *__pyx_v_self); /* proto */
 static PyObject *__pyx_pf_5renpy_4text_11textsupport_5Glyph_2__repr__(struct __pyx_obj_5renpy_4text_11textsupport_Glyph *__pyx_v_self); /* proto */
 static PyObject *__pyx_pf_5renpy_4text_11textsupport_5Glyph_1x___get__(struct __pyx_obj_5renpy_4text_11textsupport_Glyph *__pyx_v_self); /* proto */
@@ -964,7 +964,7 @@ static char __pyx_k_offset[] = "offset";
 static char __pyx_k_source[] = "source";
 static char __pyx_k_spaces[] = "spaces";
 static char __pyx_k_splitx[] = "splitx";
-static char __pyx_k_unichr[] = "unichr";
+static char __pyx_k_chr[] = "chr";
 static char __pyx_k_CLASSES[] = "CLASSES";
 static char __pyx_k_advance[] = "advance";
 static char __pyx_k_justify[] = "justify";
@@ -1190,7 +1190,7 @@ static PyObject *__pyx_n_s_text_align;
 static PyObject *__pyx_n_s_tokenize;
 static PyObject *__pyx_n_s_tpg;
 static PyObject *__pyx_n_s_tweak_glyph_spacing;
-static PyObject *__pyx_n_s_unichr;
+static PyObject *__pyx_n_s_chr;
 static PyObject *__pyx_n_s_w;
 static PyObject *__pyx_n_s_width;
 static PyObject *__pyx_n_s_x;
@@ -5652,7 +5652,7 @@ static PyObject *__pyx_pf_5renpy_4text_11textsupport_12linebreak_debug(CYTHON_UN
  *         if g.split == SPLIT_INSTEAD:
  *             rv += "|"
  *         elif g.split == SPLIT_BEFORE:             # <<<<<<<<<<<<<<
- *             rv += "[" + unichr(g.character)
+ *             rv += "[" + chr(g.character)
  *         else:
  */
     switch (__pyx_v_g->split) {
@@ -5671,7 +5671,7 @@ static PyObject *__pyx_pf_5renpy_4text_11textsupport_12linebreak_debug(CYTHON_UN
  *         if g.split == SPLIT_INSTEAD:
  *             rv += "|"             # <<<<<<<<<<<<<<
  *         elif g.split == SPLIT_BEFORE:
- *             rv += "[" + unichr(g.character)
+ *             rv += "[" + chr(g.character)
  */
       __pyx_t_3 = PyNumber_InPlaceAdd(__pyx_v_rv, __pyx_kp_s__2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 393; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
       __Pyx_GOTREF(__pyx_t_3);
@@ -5683,7 +5683,7 @@ static PyObject *__pyx_pf_5renpy_4text_11textsupport_12linebreak_debug(CYTHON_UN
  *         if g.split == SPLIT_INSTEAD:
  *             rv += "|"
  *         elif g.split == SPLIT_BEFORE:             # <<<<<<<<<<<<<<
- *             rv += "[" + unichr(g.character)
+ *             rv += "[" + chr(g.character)
  *         else:
  */
       case __pyx_e_5renpy_4text_11textsupport_SPLIT_BEFORE:
@@ -5691,9 +5691,9 @@ static PyObject *__pyx_pf_5renpy_4text_11textsupport_12linebreak_debug(CYTHON_UN
       /* "renpy/text/textsupport.pyx":395
  *             rv += "|"
  *         elif g.split == SPLIT_BEFORE:
- *             rv += "[" + unichr(g.character)             # <<<<<<<<<<<<<<
+ *             rv += "[" + chr(g.character)             # <<<<<<<<<<<<<<
  *         else:
- *             rv += unichr(g.character)
+ *             rv += chr(g.character)
  */
       __pyx_t_3 = __Pyx_PyInt_From_unsigned_int(__pyx_v_g->character); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 395; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
       __Pyx_GOTREF(__pyx_t_3);
@@ -5702,7 +5702,7 @@ static PyObject *__pyx_pf_5renpy_4text_11textsupport_12linebreak_debug(CYTHON_UN
       PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_3);
       __Pyx_GIVEREF(__pyx_t_3);
       __pyx_t_3 = 0;
-      __pyx_t_3 = __Pyx_PyObject_Call(__pyx_builtin_unichr, __pyx_t_4, NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 395; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+      __pyx_t_3 = __Pyx_PyObject_Call(__pyx_builtin_chr, __pyx_t_4, NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 395; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
       __Pyx_GOTREF(__pyx_t_3);
       __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
       __pyx_t_4 = PyNumber_Add(__pyx_kp_s__3, __pyx_t_3); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 395; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
@@ -5717,9 +5717,9 @@ static PyObject *__pyx_pf_5renpy_4text_11textsupport_12linebreak_debug(CYTHON_UN
       default:
 
       /* "renpy/text/textsupport.pyx":397
- *             rv += "[" + unichr(g.character)
+ *             rv += "[" + chr(g.character)
  *         else:
- *             rv += unichr(g.character)             # <<<<<<<<<<<<<<
+ *             rv += chr(g.character)             # <<<<<<<<<<<<<<
  * 
  *     return rv
  */
@@ -5730,7 +5730,7 @@ static PyObject *__pyx_pf_5renpy_4text_11textsupport_12linebreak_debug(CYTHON_UN
       PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_3);
       __Pyx_GIVEREF(__pyx_t_3);
       __pyx_t_3 = 0;
-      __pyx_t_3 = __Pyx_PyObject_Call(__pyx_builtin_unichr, __pyx_t_4, NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 397; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+      __pyx_t_3 = __Pyx_PyObject_Call(__pyx_builtin_chr, __pyx_t_4, NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 397; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
       __Pyx_GOTREF(__pyx_t_3);
       __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
       __pyx_t_4 = PyNumber_InPlaceAdd(__pyx_v_rv, __pyx_t_3); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 397; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
@@ -5752,7 +5752,7 @@ static PyObject *__pyx_pf_5renpy_4text_11textsupport_12linebreak_debug(CYTHON_UN
   __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 
   /* "renpy/text/textsupport.pyx":399
- *             rv += unichr(g.character)
+ *             rv += chr(g.character)
  * 
  *     return rv             # <<<<<<<<<<<<<<
  * 
@@ -10826,7 +10826,7 @@ static __Pyx_StringTabEntry __pyx_string_tab[] = {
   {&__pyx_n_s_tokenize, __pyx_k_tokenize, sizeof(__pyx_k_tokenize), 0, 0, 1, 1},
   {&__pyx_n_s_tpg, __pyx_k_tpg, sizeof(__pyx_k_tpg), 0, 0, 1, 1},
   {&__pyx_n_s_tweak_glyph_spacing, __pyx_k_tweak_glyph_spacing, sizeof(__pyx_k_tweak_glyph_spacing), 0, 0, 1, 1},
-  {&__pyx_n_s_unichr, __pyx_k_unichr, sizeof(__pyx_k_unichr), 0, 0, 1, 1},
+  {&__pyx_n_s_chr, __pyx_k_chr, sizeof(__pyx_k_chr), 0, 0, 1, 1},
   {&__pyx_n_s_w, __pyx_k_w, sizeof(__pyx_k_w), 0, 0, 1, 1},
   {&__pyx_n_s_width, __pyx_k_width, sizeof(__pyx_k_width), 0, 0, 1, 1},
   {&__pyx_n_s_x, __pyx_k_x, sizeof(__pyx_k_x), 0, 0, 1, 1},
@@ -10838,7 +10838,7 @@ static int __Pyx_InitCachedBuiltins(void) {
   __pyx_builtin_range = __Pyx_GetBuiltinName(__pyx_n_s_range); if (!__pyx_builtin_range) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 152; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
   __pyx_builtin_Exception = __Pyx_GetBuiltinName(__pyx_n_s_Exception); if (!__pyx_builtin_Exception) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 98; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
   __pyx_builtin_ord = __Pyx_GetBuiltinName(__pyx_n_s_ord); if (!__pyx_builtin_ord) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 175; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __pyx_builtin_unichr = __Pyx_GetBuiltinName(__pyx_n_s_unichr); if (!__pyx_builtin_unichr) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 395; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+  __pyx_builtin_chr = __Pyx_GetBuiltinName(__pyx_n_s_chr); if (!__pyx_builtin_chr) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 395; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
   return 0;
   __pyx_L1_error:;
   return -1;
diff --git a/module/generate_linebreak.py b/module/generate_linebreak.py
index 670adb7..8914199 100644
--- a/module/generate_linebreak.py
+++ b/module/generate_linebreak.py
@@ -41,24 +41,24 @@ other_classes = " PITCH AI BK CB CJ CR LF NL SA SG SP XX"
 
 lines = breaking.split("\n")
 
-print "# This is generated code. Do not edit."
-print
+print("# This is generated code. Do not edit.")
+print()
 
 # A map from character class to the number that represents it.
 cl = { }
 
 
 for i, j in enumerate((lines[0] + other_classes).split()):
-    print "cdef char BC_{} = {}".format(j, i)
+    print("cdef char BC_{} = {}".format(j, i))
     cl[j] = i
 
-print "CLASSES = {"
+print("CLASSES = {")
 
 for i, j in enumerate((lines[0] + other_classes).split()):
-    print "    \"{}\" : {},".format(j, i)
+    print("    \"{}\" : {},".format(j, i))
     cl[j] = i
 
-print "}"
+print("}")
 
 rules = [ ]
 
@@ -66,12 +66,12 @@ for l in lines[1:]:
     for c in l.split()[1:]:
         rules.append(c)
 
-print
-print "cdef char *break_rules = \"" + "".join(rules) + "\""
+print()
+print("cdef char *break_rules = \"" + "".join(rules) + "\"")
 
 cc = [ 'XX' ] * 65536
 
-for l in file("LineBreak.txt"):
+for l in open("LineBreak.txt"):
     m = re.match("(\w+)\.\.(\w+);(\w\w)", l)
     if m:
         start = int(m.group(1), 16)
@@ -108,7 +108,7 @@ def generate(name, func):
     assert "CJ" not in ncc
     assert "AI" not in ncc
 
-    print "cdef char *break_" + name + " = \"" + "".join("\\x%02x" % cl[i] for i in ncc) + "\""
+    print("cdef char *break_" + name + " = \"" + "".join("\\x%02x" % cl[i] for i in ncc) + "\"")
 
 def western(i, cl):
     if cl == "CJ":
diff --git a/module/generate_styles.py b/module/generate_styles.py
index e8f6957..f2942ca 100644
--- a/module/generate_styles.py
+++ b/module/generate_styles.py
@@ -19,17 +19,13 @@
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-from __future__ import print_function, unicode_literals, division, absolute_import
-
-str = unicode # @ReservedAssignment
-
 import collections
 import os
 
 try:
     from io import StringIO
 except:
-    from StringIO import StringIO
+    from io import StringIO
 
 # Paths
 BASE = os.path.dirname(os.path.abspath(__file__))
@@ -335,7 +331,7 @@ synthetic_properties = sorted_dict(
 
 all_properties = collections.OrderedDict()
 
-for k, v in style_properties.items():
+for k, v in list(style_properties.items()):
     all_properties[k] = [ (k, None) ]
 
 all_properties.update(synthetic_properties)
@@ -372,7 +368,7 @@ class CodeGen(object):
                 return
 
         with open(self.filename, "wb") as f:
-            f.write(text)
+            f.write(bytes(text, "utf-8"))
 
     def write(self, s, *args, **kwargs):
         out = "    " * self.depth
@@ -403,7 +399,7 @@ def generate_constants():
     g.write("DEF PREFIX_COUNT = {}", PREFIX_COUNT)
     g.write("DEF STYLE_PROPERTY_COUNT = {}", style_property_count)
 
-    for p in prefixes.values():
+    for p in list(prefixes.values()):
         if p.index < 0:
             continue
 
@@ -460,13 +456,13 @@ def generate_property_functions():
     This generates code that defines the property functions.
     """
 
-    for prefix in sorted(prefixes.values(), key=lambda p : p.index):
+    for prefix in sorted(list(prefixes.values()), key=lambda p : p.index):
         g = CodeGen("module/gen/style_{}functions.pyx".format(prefix.name))
 
         g.write('include "style_common.pxi"')
         g.write('')
 
-        for propname, proplist in all_properties.items():
+        for propname, proplist in list(all_properties.items()):
             generate_property_function(g, prefix, propname, proplist)
 
         g.close()
@@ -522,13 +518,13 @@ def generate_sets():
 
     ap = collections.OrderedDict()
 
-    for k, v in all_properties.items():
+    for k, v in list(all_properties.items()):
         ap[k] = [ i[0] for i in v ]
 
     prefix_priority = collections.OrderedDict()
     prefix_alts = collections.OrderedDict()
 
-    for p in prefixes.values():
+    for p in list(prefixes.values()):
         prefix_priority[p.name] = p.priority
         prefix_alts[p.name] = p.alt_names
 
diff --git a/module/maketegl.py b/module/maketegl.py
index b5e1bda..4b9edc6 100644
--- a/module/maketegl.py
+++ b/module/maketegl.py
@@ -26,8 +26,6 @@
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 
-from __future__ import print_function
-
 # Modified in 2010,2014 by PyTom to generate Cython code that uses glew.
 
 VERSION = "0.1"
diff --git a/module/pysdlsound/__init__.py b/module/pysdlsound/__init__.py
index 8f2c8e9..a349a0d 100644
--- a/module/pysdlsound/__init__.py
+++ b/module/pysdlsound/__init__.py
@@ -9,7 +9,7 @@ except:
     pass
 
 try:
-    import linmixer #@UnresolvedImport
+    from . import linmixer #@UnresolvedImport
     sys.modules['linmixer'] = sys.modules['pysdlsound.linmixer']
 except:
     pass
diff --git a/module/setup.py b/module/setup.py
index 38854d0..ea390e2 100644
--- a/module/setup.py
+++ b/module/setup.py
@@ -103,7 +103,7 @@ if has_fribidi and (not android) and (not ios):
     try:
         # Some versions of fribidi require glib, and it doesn't hurt to include it in
         # our path.
-        glib_flags = subprocess.check_output(["pkg-config", "--cflags", "glib-2.0"])
+        glib_flags = subprocess.check_output(["pkg-config", "--cflags", "glib-2.0"]).decode("utf-8")
         setuplib.extra_compile_args.extend(glib_flags.split())
     except:
         pass
@@ -232,4 +232,4 @@ import renpy
 setuplib.setup("Ren'Py", renpy.version[7:])
 
 if not has_fribidi:
-    print "Warning: Did not include fribidi."
+    print("Warning: Did not include fribidi.")
diff --git a/module/setuplib.py b/module/setuplib.py
index 3b27819..c2b6eee 100644
--- a/module/setuplib.py
+++ b/module/setuplib.py
@@ -96,9 +96,9 @@ def include(header, directory=None, optional=True):
         return False
 
     if directory is None:
-        print "Could not find required header {0}.".format(header)
+        print("Could not find required header {0}.".format(header))
     else:
-        print "Could not find required header {0}/{1}.".format(directory, header)
+        print("Could not find required header {0}/{1}.".format(directory, header))
 
     sys.exit(-1)
 
@@ -133,7 +133,7 @@ def library(name, optional=False):
     if optional:
         return False
 
-    print "Could not find required library {0}.".format(name)
+    print("Could not find required library {0}.".format(name))
     sys.exit(-1)
 
 # A list of extension objects that we use.
@@ -182,7 +182,7 @@ def cython(name, source=[], libs=[], compile_if=True, define_macros=[], pyx=None
     elif os.path.exists(fn):
         pass
     else:
-        print "Could not find {0}.".format(fn)
+        print("Could not find {0}.".format(fn))
         sys.exit(-1)
 
     module_dir = os.path.dirname(fn)
@@ -190,7 +190,7 @@ def cython(name, source=[], libs=[], compile_if=True, define_macros=[], pyx=None
     # Figure out what it depends on.
     deps = [ fn ]
 
-    f = file(fn)
+    f = open(fn)
     for l in f:
 
         m = re.search(r'from\s*([\w.]+)\s*cimport', l)
@@ -243,19 +243,19 @@ def cython(name, source=[], libs=[], compile_if=True, define_macros=[], pyx=None
         elif os.path.exists(dep_fn):
             pass
         else:
-            print "{0} depends on {1}, which can't be found.".format(fn, dep_fn)
+            print("{0} depends on {1}, which can't be found.".format(fn, dep_fn))
             sys.exit(-1)
 
         if os.path.getmtime(dep_fn) > c_mtime:
             out_of_date = True
 
     if out_of_date and not cython_command:
-        print "WARNING:", name, "is out of date, but RENPY_CYTHON isn't set."
+        print("WARNING:", name, "is out of date, but RENPY_CYTHON isn't set.")
         out_of_date = False
 
     # If the file is out of date, regenerate it.
     if out_of_date:
-        print name, "is out of date."
+        print(name, "is out of date.")
 
         try:
             import subprocess
@@ -280,10 +280,10 @@ def cython(name, source=[], libs=[], compile_if=True, define_macros=[], pyx=None
                 "-o",
                 c_fn])
 
-        except subprocess.CalledProcessError, e:
-            print
-            print str(e)
-            print
+        except subprocess.CalledProcessError as e:
+            print()
+            print(str(e))
+            print()
             sys.exit(-1)
 
     # Build the module normally once we have the c file.
@@ -299,7 +299,7 @@ def find_unnecessary_gen():
         if i in necessary_gen:
             continue
 
-        print "Unnecessary file", os.path.join("gen", i)
+        print("Unnecessary file", os.path.join("gen", i))
 
 
 py_modules = [ ]
@@ -326,14 +326,14 @@ def copyfile(source, dest, replace=None, replace_with=None):
         if os.path.getmtime(sfn) <= os.path.getmtime(dfn):
             return
 
-    sf = file(sfn, "rb")
+    sf = open(sfn, "rb")
     data = sf.read()
     sf.close()
 
     if replace:
         data = data.replace(replace, replace_with)
 
-    df = file(dfn, "wb")
+    df = open(dfn, "wb")
     df.write("# This file was automatically generated from " + source + "\n")
     df.write("# Modifications will be automatically overwritten.\n\n")
     df.write(data)
diff --git a/renpy/__init__.py b/renpy/__init__.py
index e6f5310..91a6f24 100644
--- a/renpy/__init__.py
+++ b/renpy/__init__.py
@@ -27,7 +27,7 @@ import os
 import copy
 import types
 import threading
-import cPickle
+import pickle
 
 ################################################################################
 # Version information
@@ -200,14 +200,14 @@ class Backup():
         if mobile:
             return
 
-        for m in sys.modules.values():
+        for m in list(sys.modules.values()):
             if m is None:
                 continue
 
             self.backup_module(m)
 
         # A pickled version of self.objects.
-        self.objects_pickle = cPickle.dumps(self.objects, cPickle.HIGHEST_PROTOCOL)
+        self.objects_pickle = pickle.dumps(self.objects, pickle.HIGHEST_PROTOCOL)
 
         self.objects = None
 
@@ -229,7 +229,7 @@ class Backup():
 
         self.names[mod] = set(vars(mod).keys())
 
-        for k, v in vars(mod).iteritems():
+        for k, v in vars(mod).items():
 
             if k.startswith("__") and k.endswith("__"):
                 continue
@@ -248,10 +248,10 @@ class Backup():
             # If we have a problem pickling things, uncomment the next block.
 
             try:
-                cPickle.dumps(v, cPickle.HIGHEST_PROTOCOL)
+                pickle.dumps(v, pickle.HIGHEST_PROTOCOL)
             except:
-                print "Cannot pickle", name + "." + k, "=", repr(v)
-                print "Reduce Ex is:", repr(v.__reduce_ex__(cPickle.HIGHEST_PROTOCOL))
+                print("Cannot pickle", name + "." + k, "=", repr(v))
+                print("Reduce Ex is:", repr(v.__reduce_ex__(pickle.HIGHEST_PROTOCOL)))
 
     def restore(self):
         """
@@ -263,15 +263,15 @@ class Backup():
             return
 
         # Remove new variables from the module.
-        for mod, names in self.names.iteritems():
+        for mod, names in self.names.items():
             modvars = vars(mod)
             for name in set(modvars.keys()) - names:
                 del modvars[name]
 
 
-        objects = cPickle.loads(self.objects_pickle)
+        objects = pickle.loads(self.objects_pickle)
 
-        for k, v in self.variables.iteritems():
+        for k, v in self.variables.items():
             mod, field = k
             setattr(mod, field, objects[v])
 
@@ -473,12 +473,12 @@ def post_import():
     import subprocess
     sys.modules['renpy.subprocess'] = subprocess
 
-    for k, v in renpy.defaultstore.__dict__.iteritems():
+    for k, v in renpy.defaultstore.__dict__.items():
         renpy.store.__dict__.setdefault(k, v)
 
     # Import everything into renpy.exports, provided it isn't
     # already there.
-    for k, v in globals().iteritems():
+    for k, v in globals().items():
         vars(renpy.exports).setdefault(k, v)
 
 
@@ -519,7 +519,7 @@ def reload_all():
     renpy.display.interface = None
 
     # Delete the store modules.
-    for i in sys.modules.keys():
+    for i in list(sys.modules.keys()):
         if i.startswith("store") or i == "renpy.store":
             m = sys.modules[i]
 
diff --git a/renpy/add_from.py b/renpy/add_from.py
index 9935b29..5b2e3b5 100644
--- a/renpy/add_from.py
+++ b/renpy/add_from.py
@@ -82,7 +82,7 @@ def process_file(fn):
     consumed = 0
 
     # The output.
-    output = u""
+    output = ""
 
     for position, target in edits:
         output += data[consumed:position]
diff --git a/renpy/ast.py b/renpy/ast.py
index ec2cbf5..286c54a 100644
--- a/renpy/ast.py
+++ b/renpy/ast.py
@@ -30,7 +30,8 @@ import renpy.display
 
 import re
 import time
-import md5
+import hashlib
+import collections
 
 def statement_name(name):
     """
@@ -102,7 +103,7 @@ class ParameterInfo(object):
 
         extrapos = tuple(args[len(self.positional):])
 
-        for name, value in kwargs.iteritems():
+        for name, value in kwargs.items():
             if name in values:
                 if not ignore_errors:
                     raise Exception("Parameter %s has two values." % name)
@@ -133,7 +134,7 @@ class ParameterInfo(object):
         if self.extrakw:
             rv[self.extrakw] = values
         elif values and not ignore_errors:
-            raise Exception("Unknown keyword arguments: %s" % ( ", ".join(values.keys())))
+            raise Exception("Unknown keyword arguments: %s" % ( ", ".join(list(values.keys()))))
 
         return rv
 
@@ -192,7 +193,7 @@ def __newobj__(cls, *args):
     return cls.__new__(cls, *args)
 
 # This represents a string containing python code.
-class PyExpr(unicode):
+class PyExpr(str):
 
     __slots__ = [
         'filename',
@@ -200,14 +201,14 @@ class PyExpr(unicode):
         ]
 
     def __new__(cls, s, filename, linenumber):
-        self = unicode.__new__(cls, s)
+        self = str.__new__(cls, s)
         self.filename = filename
         self.linenumber = linenumber
 
         return self
 
     def __getnewargs__(self):
-        return (unicode(self), self.filename, self.linenumber) # E1101
+        return (str(self), self.filename, self.linenumber) # E1101
 
 class PyCode(object):
 
@@ -261,7 +262,7 @@ class PyCode(object):
         if isinstance(code, renpy.python.ast.AST): #@UndefinedVariable
             code = renpy.python.ast.dump(code) #@UndefinedVariable
 
-        self.hash = chr(renpy.bytecode_version) + md5.md5(repr(self.location) + code.encode("utf-8")).digest()
+        self.hash = chr(renpy.bytecode_version) + hashlib.md5(repr(self.location) + code.encode("utf-8")).digest()
         return self.hash
 
 
@@ -291,7 +292,7 @@ class Scry(object):
     def __getattr__(self, name):
         return None
 
-    def next(self): #@ReservedAssignment
+    def __next__(self): #@ReservedAssignment
         if self._next is None:
             return None
         else:
@@ -382,7 +383,7 @@ class Node(object):
         node.
         """
 
-        if self.next is old:
+        if self.__next__ is old:
             self.next = new
 
     def execute(self):
@@ -409,8 +410,8 @@ class Node(object):
         renpy.display.predict.screen to be called as necessary.
         """
 
-        if self.next:
-            return [ self.next ]
+        if self.__next__:
+            return [ self.__next__ ]
         else:
             return [ ]
 
@@ -421,7 +422,7 @@ class Node(object):
         """
 
         rv = Scry()
-        rv._next = self.next # W0201
+        rv._next = self.__next__ # W0201
         return rv
 
     def restructure(self, callback):
@@ -573,7 +574,7 @@ class Say(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("say")
 
         try:
@@ -584,8 +585,8 @@ class Say(Node):
 
             if not (
                 (who is None) or
-                callable(who) or
-                isinstance(who, basestring) ):
+                isinstance(who, collections.Callable) or
+                isinstance(who, str) ):
 
                 raise Exception("Sayer %s is not a function or string." % self.who.encode("utf-8"))
 
@@ -630,7 +631,7 @@ class Say(Node):
         finally:
             renpy.game.context().say_attributes = old_attributes
 
-        return [ self.next ]
+        return [ self.__next__ ]
 
     def scry(self):
         rv = Node.scry(self)
@@ -685,7 +686,7 @@ class Init(Node):
         chain_block(self.block, None)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("init")
 
     def restructure(self, callback):
@@ -743,14 +744,14 @@ class Label(Node):
             self.next = next
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("label")
 
         renpy.game.context().mark_seen()
 
         values = apply_arguments(self.parameters, renpy.store._args, renpy.store._kwargs)
 
-        for k, v in values.iteritems():
+        for k, v in values.items():
             renpy.exports.dynamic(k)
             setattr(renpy.store, k, v)
 
@@ -798,7 +799,7 @@ class Python(Node):
         renpy.python.create_store(self.store)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("python")
 
         try:
@@ -845,7 +846,7 @@ class EarlyPython(Node):
         return (EarlyPython, self.code.source)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("python early")
 
     def early_execute(self):
@@ -889,7 +890,7 @@ class Image(Node):
         # Note: We should always check that self.code is None before
         # accessing self.atl, as self.atl may not always exist.
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("image")
 
         if self.code is not None:
@@ -933,7 +934,7 @@ class Transform(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("transform")
 
         parameters = getattr(self, "parameters", None)
@@ -1058,14 +1059,14 @@ class Show(Node):
         return (Show, tuple(self.imspec[0]))
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("show")
 
         show_imspec(self.imspec, atl=getattr(self, "atl", None))
 
     def predict(self):
         predict_imspec(self.imspec, atl=getattr(self, "atl", None))
-        return [ self.next ]
+        return [ self.__next__ ]
 
     def analyze(self):
         if getattr(self, 'atl', None) is not None:
@@ -1091,7 +1092,7 @@ class ShowLayer(Node):
         return (ShowLayer, self.layer)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("show layer")
 
         at_list = [ renpy.python.py_eval(i) for i in self.at_list ]
@@ -1103,7 +1104,7 @@ class ShowLayer(Node):
         renpy.exports.layer_at_list(at_list, layer=self.layer)
 
     def predict(self):
-        return [ self.next ]
+        return [ self.__next__ ]
 
     def analyze(self):
         if self.atl is not None:
@@ -1142,7 +1143,7 @@ class Scene(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("scene")
 
         renpy.config.scene(self.layer)
@@ -1155,7 +1156,7 @@ class Scene(Node):
         if self.imspec:
             predict_imspec(self.imspec, atl=getattr(self, "atl", None), scene=True)
 
-        return [ self.next ]
+        return [ self.__next__ ]
 
     def analyze(self):
         if getattr(self, 'atl', None) is not None:
@@ -1204,11 +1205,11 @@ class Hide(Node):
 
         renpy.game.context().images.predict_hide(tag, layer)
 
-        return [ self.next ]
+        return [ self.__next__ ]
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("hide")
 
         if len(self.imspec) == 3:
@@ -1252,7 +1253,7 @@ class With(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("with")
 
         trans = renpy.python.py_eval(self.expr)
@@ -1276,7 +1277,7 @@ class With(Node):
             pass
 
 
-        return [ self.next ]
+        return [ self.__next__ ]
 
 
 class Call(Node):
@@ -1423,7 +1424,7 @@ class Menu(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("menu")
 
         choices = [ ]
@@ -1452,7 +1453,7 @@ class Menu(Node):
         if choice is not None:
             next_node(self.items[choice][2][0])
         else:
-            next_node(self.next)
+            next_node(self.__next__)
 
 
     def predict(self):
@@ -1547,7 +1548,7 @@ class Pass(Node):
         return (Pass,)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("pass")
 
 
@@ -1585,14 +1586,14 @@ class While(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("while")
 
         if renpy.python.py_eval(self.condition):
             next_node(self.block[0])
 
     def predict(self):
-        return [ self.block[0], self.next ]
+        return [ self.block[0], self.__next__ ]
 
     def scry(self):
         rv = Node.scry(self)
@@ -1640,7 +1641,7 @@ class If(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("if")
 
         for condition, block in self.entries:
@@ -1651,7 +1652,7 @@ class If(Node):
     def predict(self):
 
         return [ block[0] for _condition, block in self.entries ] + \
-               [ self.next ]
+               [ self.__next__ ]
 
     def scry(self):
         rv = Node.scry(self)
@@ -1726,7 +1727,7 @@ class UserStatement(Node):
         if rv is not None:
             return renpy.game.script.lookup(rv)
         else:
-            return self.next
+            return self.__next__
 
     def scry(self):
         rv = Node.scry(self)
@@ -1787,7 +1788,7 @@ class Define(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("define")
 
         value = renpy.python.py_eval_bytecode(self.code.bytecode)
@@ -1834,7 +1835,7 @@ class Default(Node):
 
     def execute(self):
 
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("default")
 
         default_statements.append(self)
@@ -1889,7 +1890,7 @@ class Screen(Node):
         return (Screen, self.screen.name)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("screen")
 
         self.screen.define((self.filename, self.linenumber))
@@ -1956,7 +1957,7 @@ class Translate(Node):
         statement_name("translate")
 
         if self.language is not None:
-            next_node(self.next)
+            next_node(self.__next__)
             raise Exception("Translation nodes cannot be run directly.")
 
         if self.identifier not in renpy.game.persistent._seen_translates: # @UndefinedVariable
@@ -2000,7 +2001,7 @@ class EndTranslate(Node):
         return (EndTranslate,)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("end translate")
 
         renpy.game.context().translate_identifier = None
@@ -2030,7 +2031,7 @@ class TranslateString(Node):
         return (TranslateString,)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("translate string")
 
         renpy.translation.add_string_translation(self.language, self.old, self.new)
@@ -2066,7 +2067,7 @@ class TranslatePython(Node):
         return (TranslatePython, self.code.source)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("translate_python")
 
     # def early_execute(self):
@@ -2106,7 +2107,7 @@ class TranslateBlock(Node):
         chain_block(self.block, None)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("translate_block")
 
     def restructure(self, callback):
@@ -2156,7 +2157,7 @@ class Style(Node):
         return (Style, self.style_name)
 
     def execute(self):
-        next_node(self.next)
+        next_node(self.__next__)
         statement_name("style")
 
         if self.variant is not None:
@@ -2180,7 +2181,7 @@ class Style(Node):
 
         if self.properties:
             properties = { }
-            for name, expr in self.properties.items():
+            for name, expr in list(self.properties.items()):
                 properties[name] = renpy.python.py_eval(expr)
 
             s.add_properties(properties)
diff --git a/renpy/atl.py b/renpy/atl.py
index d0f9d31..582f531 100644
--- a/renpy/atl.py
+++ b/renpy/atl.py
@@ -39,7 +39,7 @@ def executing(loc):
 warpers = { }
 
 def atl_warper(f):
-    name = f.func_name
+    name = f.__name__
     warpers[name] = f
     return f
 
@@ -135,7 +135,7 @@ def interpolate(t, a, b, type): #@ReservedAssignment
         return tuple(interpolate(t, i, j, ty) for i, j, ty in zip(a, b, type))
 
     # Deal with booleans, nones, etc.
-    elif b is None or isinstance(b, (bool, basestring)):
+    elif b is None or isinstance(b, (bool, str)):
         if t >= 1.0:
             return b
         else:
@@ -375,7 +375,7 @@ class ATLTransformBase(renpy.object.Object):
             raise Exception("Too many arguments passed to ATL transform.")
 
         # Handle keyword arguments.
-        for k, v in kwargs.iteritems():
+        for k, v in kwargs.items():
 
             if k in positional:
                 positional.remove(k)
@@ -1113,7 +1113,7 @@ class Interpolation(Statement):
             linear, revolution, splines = state
 
         # Linearly interpolate between the things in linear.
-        for k, (old, new) in linear.iteritems():
+        for k, (old, new) in linear.items():
             value = interpolate(complete, old, new, PROPERTIES[k])
 
             setattr(trans.state, k, value)
@@ -1356,19 +1356,19 @@ class RawOn(RawStatement):
 
         handlers = { }
 
-        for k, v in self.handlers.iteritems():
+        for k, v in self.handlers.items():
             handlers[k] = v.compile(ctx)
 
         return On(self.loc, handlers)
 
     def predict(self, ctx):
-        for i in self.handlers.itervalues():
+        for i in self.handlers.values():
             i.predict(ctx)
 
     def mark_constant(self):
         constant = GLOBAL_CONST
 
-        for block in self.handlers.itervalues():
+        for block in self.handlers.values():
             block.mark_constant()
             constant = min(constant, block.constant)
 
@@ -1450,7 +1450,7 @@ class On(Statement):
                 return "event", (name, arg), None
 
     def visit(self):
-        return [ j for i in self.handlers.itervalues() for j in i.visit() ]
+        return [ j for i in self.handlers.values() for j in i.visit() ]
 
 
 # Event statement.
diff --git a/renpy/audio/androidhw.py b/renpy/audio/androidhw.py
index 1c3b6ed..e487b56 100644
--- a/renpy/audio/androidhw.py
+++ b/renpy/audio/androidhw.py
@@ -79,7 +79,7 @@ class AndroidVideoChannel(object):
 
         filename = self.queue.pop(0)
 
-        print "Playing", filename
+        print("Playing", filename)
 
         f = renpy.loader.load(filename)
 
diff --git a/renpy/audio/audio.py b/renpy/audio/audio.py
index d265a3a..f322aab 100644
--- a/renpy/audio/audio.py
+++ b/renpy/audio/audio.py
@@ -803,7 +803,7 @@ def pause_all():
     Pause all playback channels.
     """
 
-    for c in channels.values():
+    for c in list(channels.values()):
         c.pause()
 
 def unpause_all():
@@ -811,5 +811,5 @@ def unpause_all():
     Unpause all playback channels.
     """
 
-    for c in channels.values():
+    for c in list(channels.values()):
         c.unpause()
diff --git a/renpy/audio/music.py b/renpy/audio/music.py
index bf959b5..9ccce1c 100644
--- a/renpy/audio/music.py
+++ b/renpy/audio/music.py
@@ -76,7 +76,7 @@ def play(filenames, channel="music", loop=None, fadeout=None, synchro_start=Fals
     if filenames is None:
         return
 
-    if isinstance(filenames, basestring):
+    if isinstance(filenames, str):
         filenames = [ filenames ]
 
     try:
@@ -151,7 +151,7 @@ def queue(filenames, channel="music", loop=None, clear_queue=True, fadein=0, tig
         filenames = [ ]
         loop = False
 
-    if isinstance(filenames, basestring):
+    if isinstance(filenames, str):
         filenames = [ filenames ]
 
     try:
diff --git a/renpy/bootstrap.py b/renpy/bootstrap.py
index 9dbe2e0..9261c2b 100644
--- a/renpy/bootstrap.py
+++ b/renpy/bootstrap.py
@@ -64,8 +64,8 @@ def extra_imports():
     import compiler; compiler
     import textwrap; textwrap
     import copy; copy
-    import urllib; urllib
-    import urllib2; urllib2
+    import urllib.request, urllib.parse, urllib.error; urllib
+    import urllib.request, urllib.error, urllib.parse; urllib2
     import codecs; codecs
     import rsa; rsa
     import decimal; decimal
@@ -102,14 +102,14 @@ trace_local = None
 
 def trace_function(frame, event, arg):
     fn = os.path.basename(frame.f_code.co_filename)
-    print >>trace_file, fn, frame.f_lineno, frame.f_code.co_name, event
+    print(fn, frame.f_lineno, frame.f_code.co_name, event, file=trace_file)
     return trace_local
 
 def enable_trace(level):
     global trace_file
     global trace_local
 
-    trace_file = file("trace.txt", "w", 1)
+    trace_file = open("trace.txt", "w", 1)
 
     if level > 1:
         trace_local = trace_function
@@ -137,13 +137,13 @@ def bootstrap(renpy_base):
     if os.environ.get("SDL_VIDEODRIVER", "") == "windib":
         del os.environ["SDL_VIDEODRIVER"]
 
-    renpy_base = unicode(renpy_base, FSENCODING, "replace")
+    renpy_base = str(renpy_base.encode(errors="replace"), FSENCODING, "replace")
 
     # If environment.txt exists, load it into the os.environ dictionary.
     if os.path.exists(renpy_base + "/environment.txt"):
         evars = { }
-        execfile(renpy_base + "/environment.txt", evars)
-        for k, v in evars.iteritems():
+        exec(compile(open(renpy_base + "/environment.txt").read(), renpy_base + "/environment.txt", 'exec'), evars)
+        for k, v in evars.items():
             if k not in os.environ:
                 os.environ[k] = str(v)
 
@@ -155,8 +155,8 @@ def bootstrap(renpy_base):
 
         if os.path.exists(alt_path + "/environment.txt"):
             evars = { }
-            execfile(alt_path + "/environment.txt", evars)
-            for k, v in evars.iteritems():
+            exec(compile(open(alt_path + "/environment.txt").read(), alt_path + "/environment.txt", 'exec'), evars)
+            for k, v in evars.items():
                 if k not in os.environ:
                     os.environ[k] = str(v)
 
@@ -226,14 +226,14 @@ def bootstrap(renpy_base):
         import pygame_sdl2
         pygame_sdl2.import_as_pygame()
     except:
-        print >>sys.stderr, """\
+        print("""\
 Could not import pygame_sdl2. Please ensure that this program has been built
 and unpacked properly. Also, make sure that the directories containing
 this program do not contain : or ; in their names.
 
 You may be using a system install of python. Please run {0}.sh,
 {0}.exe, or {0}.app instead.
-""".format(name)
+""".format(name), file=sys.stderr)
 
         raise
 
@@ -241,13 +241,13 @@ You may be using a system install of python. Please run {0}.sh,
     try:
         import _renpy; _renpy
     except:
-        print >>sys.stderr, """\
+        print("""\
 Could not import _renpy. Please ensure that this program has been built
 and unpacked properly.
 
 You may be using a system install of python. Please run {0}.sh,
 {0}.exe, or {0}.app instead.
-""".format(name)
+""".format(name), file=sys.stderr)
         raise
 
     # Load up all of Ren'Py, in the right order.
@@ -276,7 +276,7 @@ You may be using a system install of python. Please run {0}.sh,
                     renpy.config.logdir = basedir
 
                 if not os.path.exists(renpy.config.logdir):
-                    os.makedirs(renpy.config.logdir, 0777)
+                    os.makedirs(renpy.config.logdir, 0o777)
 
                 renpy.main.main()
 
@@ -304,7 +304,7 @@ You may be using a system install of python. Please run {0}.sh,
             except renpy.game.ParseErrorException:
                 pass
 
-            except Exception, e:
+            except Exception as e:
                 renpy.error.report_exception(e)
                 pass
 
diff --git a/renpy/character.py b/renpy/character.py
index 2d36bbc..1fc049d 100644
--- a/renpy/character.py
+++ b/renpy/character.py
@@ -56,12 +56,12 @@ class DialogueTextTags(object):
         while True:
 
             try:
-                self.text += i.next()
+                self.text += next(i)
 
-                quoted = i.next()
-                full_tag = i.next()
-                tag = i.next()
-                value = i.next()
+                quoted = next(i)
+                full_tag = next(i)
+                tag = next(i)
+                value = next(i)
 
                 if value is not None:
                     value = float(value)
@@ -152,7 +152,7 @@ def compute_widget_properties(who_args, what_args, window_args, variant=None):
 
         style = d["style"]
 
-        if isinstance(style, basestring):
+        if isinstance(style, str):
             style = getattr(renpy.store.style, style)
 
             if variant is not None:
@@ -230,7 +230,7 @@ def show_display_say(who, what, who_args={}, what_args={}, window_args={},
 
     def merge_style(style, properties):
 
-        if isinstance(style, basestring):
+        if isinstance(style, str):
             style = getattr(renpy.store.style, style)
 
         if variant is not None:
@@ -764,7 +764,7 @@ class ADVCharacter(object):
         if not (self.condition is None or renpy.python.py_eval(self.condition)):
             return True
 
-        if not isinstance(what, basestring):
+        if not isinstance(what, str):
             raise Exception("Character expects its what argument to be a string, got %r." % (what,))
 
         self.resolve_say_attributes(False)
@@ -831,7 +831,7 @@ class ADVCharacter(object):
             self.do_done(who, what)
 
             # Finally, log this line of dialogue.
-            if who and isinstance(who, (str, unicode)):
+            if who and isinstance(who, str):
                 renpy.exports.log(who)
             renpy.exports.log(what)
             renpy.exports.log("")
diff --git a/renpy/color.py b/renpy/color.py
index 4bde2af..8df0c64 100644
--- a/renpy/color.py
+++ b/renpy/color.py
@@ -123,7 +123,7 @@ class Color(tuple):
                 if len(c) == 3:
                     return tuple.__new__(cls, c + (int(255 * alpha),))
 
-            if isinstance(c, basestring):
+            if isinstance(c, str):
                 if c[0] == '#':
                     c = c[1:]
 
@@ -308,7 +308,7 @@ class Color(tuple):
         `other` may be a string, Color or an HSV tuple.
         """
 
-        if isinstance(other, basestring):
+        if isinstance(other, str):
             other = Color(other, alpha=self.alpha)
         elif not isinstance(other, Color):
             other = Color(hsv=other, alpha=self.alpha)
@@ -330,7 +330,7 @@ class Color(tuple):
         `other` may be a string, Color or an HLS tuple.
         """
 
-        if isinstance(other, basestring):
+        if isinstance(other, str):
             other = Color(other, alpha=self.alpha)
         elif not isinstance(other, Color):
             other = Color(hls=other, alpha=self.alpha)
diff --git a/renpy/common/00updater.rpy b/renpy/common/00updater.rpy
index b18dce0..6a35d66 100644
--- a/renpy/common/00updater.rpy
+++ b/renpy/common/00updater.rpy
@@ -93,7 +93,7 @@ init -1500 python in updater:
         time.sleep(3)
 
         try:
-            log = file(DEFERRED_UPDATE_LOG, "ab")
+            log = open(DEFERRED_UPDATE_LOG, "ab")
         except:
             log = StringIO.StringIO()
 
@@ -919,7 +919,7 @@ init -1500 python in updater:
                     break
 
                 try:
-                    f = file(new_fn + ".part", "rb")
+                    f = open(new_fn + ".part", "rb")
                 except:
                     self.log.write("partfile does not exist\n")
                     continue
@@ -1046,7 +1046,7 @@ init -1500 python in updater:
                 # Extract regular files.
                 tff = tf.extractfile(info)
                 new_path = path + ".new"
-                f = file(new_path, "wb")
+                f = open(new_path, "wb")
 
                 while True:
                     data = tff.read(1024 * 1024)
diff --git a/renpy/common/_developer/developer.rpym b/renpy/common/_developer/developer.rpym
index 27922d0..d3377c4 100644
--- a/renpy/common/_developer/developer.rpym
+++ b/renpy/common/_developer/developer.rpym
@@ -496,7 +496,7 @@ label _filename_list:
 
     python hide:
         import os
-        f = file("files.txt", "w")
+        f = open("files.txt", "w")
 
         for dirname, dirs, files in os.walk(config.gamedir):
 
diff --git a/renpy/curry.py b/renpy/curry.py
index f7266bf..4b47eaa 100644
--- a/renpy/curry.py
+++ b/renpy/curry.py
@@ -35,7 +35,7 @@ class Curry(object):
 
     def __call__(self, *args, **kwargs):
         return self.callable(*(self.args + args),
-                             **dict(self.kwargs.items() + kwargs.items()))
+                             **dict(list(self.kwargs.items()) + list(kwargs.items())))
     def __repr__(self):
         return "<curry %s %r %r>" % (self.callable, self.args, self.kwargs)
 
diff --git a/renpy/display/anim.py b/renpy/display/anim.py
index 70c8fe4..fc31002 100644
--- a/renpy/display/anim.py
+++ b/renpy/display/anim.py
@@ -120,7 +120,7 @@ class Edge(object):
         self.prob = prob
 
     def add(self, sma):
-        for _i in xrange(0, self.prob):
+        for _i in range(0, self.prob):
             sma.edges.setdefault(self.old, []).append(self)
 
 
@@ -201,7 +201,7 @@ class SMAnimation(renpy.display.core.Displayable):
         self.state = None
 
     def visit(self):
-        return [ i.image for i in self.states.itervalues() ]
+        return [ i.image for i in self.states.values() ]
 
     def pick_edge(self, state):
         """
@@ -304,10 +304,10 @@ class SMAnimation(renpy.display.core.Displayable):
 
         args = [ ]
 
-        for state in self.states.itervalues():
+        for state in self.states.values():
             args.append(state.motion_copy(child))
 
-        for edges in self.edges.itervalues():
+        for edges in self.edges.values():
             args.extend(edges)
 
         return SMAnimation(self.initial, delay=self.delay, *args, **self.properties)
diff --git a/renpy/display/behavior.py b/renpy/display/behavior.py
index e325abb..7c8849b 100644
--- a/renpy/display/behavior.py
+++ b/renpy/display/behavior.py
@@ -30,6 +30,7 @@ from renpy.display.render import render, Render
 import pygame_sdl2 as pygame
 
 import math
+import collections
 
 def compile_event(key, keydown):
     """
@@ -395,7 +396,7 @@ class Keymap(renpy.display.layout.Null):
 
     def event(self, ev, x, y, st):
 
-        for name, action in self.keymap.iteritems():
+        for name, action in self.keymap.items():
             if map_event(ev, name):
 
                 if self.style.activate_sound:
@@ -409,7 +410,7 @@ class Keymap(renpy.display.layout.Null):
                 raise renpy.display.core.IgnoreEvent()
 
     def predict_one_action(self):
-        for i in self.keymap.itervalues():
+        for i in self.keymap.values():
             predict_action(i)
 
 
@@ -634,7 +635,7 @@ class Button(renpy.display.layout.Window):
         predict_action(self.alternate)
 
         if self.keymap:
-            for v in self.keymap.itervalues():
+            for v in self.keymap.values():
                 predict_action(v)
 
     def render(self, width, height, st, at):
@@ -663,7 +664,7 @@ class Button(renpy.display.layout.Window):
                 try:
                     mask = renpy.display.render.render(mask, rv.width, rv.height, st, at)
                 except:
-                    if callable(mask):
+                    if isinstance(mask, collections.Callable):
                         mask = mask
                     else:
                         raise Exception("Focus_mask must be None, True, a displayable, or a callable.")
@@ -776,7 +777,7 @@ class Button(renpy.display.layout.Window):
             return None
 
         # Check the keymap.
-        for name, action in self.keymap.iteritems():
+        for name, action in self.keymap.items():
             if map_event(ev, name):
                 return run(action)
 
@@ -881,7 +882,7 @@ class ImageButton(Button):
                                           **properties)
 
     def visit(self):
-        return self.state_children.values()
+        return list(self.state_children.values())
 
     def get_child(self):
         return self.style.child or self.state_children[self.style.prefix]
@@ -910,8 +911,8 @@ class Input(renpy.text.text.Text): #@UndefinedVariable
     caret_pos = 0
     old_caret_pos = 0
     pixel_width = None
-    default = u""
-    edit_text = u""
+    default = ""
+    edit_text = ""
 
     def __init__(self,
                  default="",
@@ -930,7 +931,7 @@ class Input(renpy.text.text.Text): #@UndefinedVariable
 
         super(Input, self).__init__("", style=style, replaces=replaces, substitute=False, **properties)
 
-        self.default = unicode(default)
+        self.default = str(default)
         self.content = self.default
 
         self.length = length
@@ -1017,7 +1018,7 @@ class Input(renpy.text.text.Text): #@UndefinedVariable
         def set_content(content):
 
             if content == "":
-                content = u"\u200b"
+                content = "\u200b"
 
             if editable:
                 l = len(content)
@@ -1131,8 +1132,8 @@ class Input(renpy.text.text.Text): #@UndefinedVariable
             raw_text = ev.text
 
         elif ev.type == pygame.KEYDOWN:
-            if ev.unicode and ord(ev.unicode[0]) >= 32:
-                raw_text = ev.unicode
+            if ev.str and ord(ev.str[0]) >= 32:
+                raw_text = ev.str
             elif renpy.display.interface.text_event_in_queue():
                 raw_text = ''
 
diff --git a/renpy/display/core.py b/renpy/display/core.py
index 5462336..3b708b8 100644
--- a/renpy/display/core.py
+++ b/renpy/display/core.py
@@ -31,7 +31,7 @@ import pygame_sdl2 as pygame
 import sys
 import os
 import time
-import cStringIO
+import io
 import threading
 
 import_time = time.time()
@@ -276,7 +276,7 @@ class Displayable(renpy.object.Object):
         return self.__class__.__name__
 
     def __repr__(self):
-        return "<{} at {:x}>".format(unicode(self).encode("utf-8"), id(self))
+        return "<{} at {:x}>".format(str(self).encode("utf-8"), id(self))
 
     def find_focusable(self, callback, focus_name):
 
@@ -963,7 +963,7 @@ class SceneLists(renpy.object.Object):
         """
 
         rv = [ ]
-        for l in self.layers.itervalues():
+        for l in self.layers.values():
             for sle in l:
                 rv.append(sle.displayable)
 
@@ -976,7 +976,7 @@ class SceneLists(renpy.object.Object):
         be displayed, or everything will be removed.
         """
 
-        for i in reversed(xrange(len(self.layers[layer]))):
+        for i in reversed(range(len(self.layers[layer]))):
 
             sle = self.layers[layer][i]
 
@@ -1031,7 +1031,7 @@ class SceneLists(renpy.object.Object):
 
             # Have to iterate in reverse order, since otherwise
             # the indexes might change.
-            for i in reversed(xrange(len(self.layers[layer]))):
+            for i in reversed(range(len(self.layers[layer]))):
                 self.hide_or_replace(layer, i, hide)
 
         self.at_list[layer].clear()
@@ -1047,10 +1047,10 @@ class SceneLists(renpy.object.Object):
         time with the given time.
         """
 
-        for l, (t, list) in self.layer_at_list.items(): #@ReservedAssignment
+        for l, (t, list) in list(self.layer_at_list.items()): #@ReservedAssignment
             self.layer_at_list[l] = (t or time, list)
 
-        for l, ll in self.layers.iteritems():
+        for l, ll in self.layers.items():
             self.layers[l] = [ i.update_time(time) for i in ll ]
 
     def showing(self, layer, name):
@@ -1590,7 +1590,7 @@ class Interface(object):
         renpy.display.log.write(s)
 
         if renpy.android and not renpy.config.log_to_stdout:
-            print s
+            print(s)
 
     def post_init(self):
         """
@@ -1893,7 +1893,7 @@ class Interface(object):
 
         self.screenshot_surface = surf
 
-        sio = cStringIO.StringIO()
+        sio = io.StringIO()
         renpy.display.module.save_png(surf, sio, 0)
         self.screenshot = sio.getvalue()
         sio.close()
@@ -2006,7 +2006,7 @@ class Interface(object):
         scene_lists = renpy.game.context().scene_lists
 
         # Compute the scene.
-        for layer, d in self.compute_scene(scene_lists).iteritems():
+        for layer, d in self.compute_scene(scene_lists).items():
             if layer not in self.transition:
                 self.old_scene[layer] = d
 
@@ -2243,7 +2243,7 @@ class Interface(object):
 
         renpy.exports.free_memory()
 
-        print "Entered background."
+        print("Entered background.")
 
         while True:
             ev = pygame.event.wait()
@@ -2254,7 +2254,7 @@ class Interface(object):
             if ev.type == pygame.APP_TERMINATING:
                 sys.exit(0)
 
-        print "Entering foreground."
+        print("Entering foreground.")
 
         # Since we came back to life, we can get rid of the
         # auto-reload.
@@ -2583,7 +2583,7 @@ class Interface(object):
         renpy.display.tts.set_root(scene[None])
 
         # If necessary, load all images here.
-        for w in scene.itervalues():
+        for w in scene.values():
             try:
                 renpy.display.predict.displayable(w)
             except:
@@ -2766,7 +2766,7 @@ class Interface(object):
 
                     if first_pass:
                         scene_lists.set_times(self.interact_time)
-                        for k, v in self.transition_time.iteritems():
+                        for k, v in self.transition_time.items():
                             if v is None:
                                 self.transition_time[k] = self.interact_time
 
@@ -2777,8 +2777,8 @@ class Interface(object):
                         new_time = get_time()
 
                         if self.profile_once or (new_time - self.profile_time > .015):
-                            print "Profile: Redraw took %.3f ms." % (1000 * (new_time - self.frame_time))
-                            print "Profile: %.3f ms to complete event." % (1000 * (new_time - self.profile_time))
+                            print("Profile: Redraw took %.3f ms." % (1000 * (new_time - self.frame_time)))
+                            print("Profile: %.3f ms to complete event." % (1000 * (new_time - self.profile_time)))
 
                         self.profile_once = False
 
diff --git a/renpy/display/dragdrop.py b/renpy/display/dragdrop.py
index 8816ee0..34aed98 100644
--- a/renpy/display/dragdrop.py
+++ b/renpy/display/dragdrop.py
@@ -28,6 +28,7 @@ from renpy.display.core import absolute
 from renpy.display.behavior import map_event, run, run_unhovered
 
 import pygame_sdl2 as pygame
+import collections
 
 def default_drag_group():
     """
@@ -449,7 +450,7 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
                 try:
                     mask = renpy.display.render.render(mask, fw, fh, st, at)
                 except:
-                    if callable(mask):
+                    if isinstance(mask, collections.Callable):
                         mask = mask
                     else:
                         raise Exception("Focus_mask must be None, True, a displayable, or a callable.")
diff --git a/renpy/display/im.py b/renpy/display/im.py
index de4a043..52f22d5 100644
--- a/renpy/display/im.py
+++ b/renpy/display/im.py
@@ -27,7 +27,7 @@ import renpy.display
 
 import math
 import zipfile
-import cStringIO
+import io
 import threading
 import time
 
@@ -266,7 +266,7 @@ class Cache(object):
         # If we're outside the cache limit, we need to go and start
         # killing off some of the entries until we're back inside it.
 
-        for ce in sorted(self.cache.itervalues(), key=lambda a : a.time):
+        for ce in sorted(iter(self.cache.values()), key=lambda a : a.time):
 
             if ce.time == self.time:
                 # If we're bigger than the limit, and there's nothing
@@ -375,7 +375,7 @@ class Cache(object):
 
                 # Remove things that are not in the workset from the pin cache,
                 # and remove things that are in the workset from pin cache.
-                for i in self.pin_cache.keys():
+                for i in list(self.pin_cache.keys()):
 
                     if i in workset:
                         workset.remove(i)
@@ -509,9 +509,9 @@ class Image(ImageBase):
 
     def __unicode__(self):
         if len(self.filename) < 20:
-            return u"Image %r" % self.filename
+            return "Image %r" % self.filename
         else:
-            return u"Image \u2026%s" % self.filename[-20:]
+            return "Image \u2026%s" % self.filename[-20:]
 
 
     def get_hash(self):
@@ -530,7 +530,7 @@ class Image(ImageBase):
 
             return surf
 
-        except Exception, e:
+        except Exception as e:
 
             if renpy.config.missing_image_callback:
                 im = renpy.config.missing_image_callback(self.filename)
@@ -565,7 +565,7 @@ class ZipFileImage(ImageBase):
         try:
             zf = zipfile.ZipFile(self.zipfilename, 'r')
             data = zf.read(self.filename)
-            sio = cStringIO.StringIO(data)
+            sio = io.StringIO(data)
             rv = renpy.display.pgrender.load_image(sio, self.filename)
             zf.close()
             return rv
@@ -1562,7 +1562,7 @@ def image(arg, loose=False, **properties):
     if isinstance(arg, ImageBase):
         return arg
 
-    elif isinstance(arg, basestring):
+    elif isinstance(arg, str):
         return Image(arg, **properties)
 
     elif isinstance(arg, renpy.display.image.ImageReference):
@@ -1608,7 +1608,7 @@ def load_surface(im):
 
 
 def reset_module():
-    print "Resetting cache."
+    print("Resetting cache.")
 
     global cache
     cache = Cache()
diff --git a/renpy/display/image.py b/renpy/display/image.py
index e5ae353..64574c3 100644
--- a/renpy/display/image.py
+++ b/renpy/display/image.py
@@ -45,7 +45,7 @@ def get_available_image_tags():
     Returns a list of image tags that have been defined.
     """
 
-    return [ k for k, v in image_attributes.items() if v ]
+    return [ k for k, v in list(image_attributes.items()) if v ]
 
 def get_available_image_attributes(tag, attributes=()):
     """
@@ -149,7 +149,7 @@ class ImageReference(renpy.display.core.Displayable):
         self.name = name
 
     def __unicode__(self):
-        return u"<ImageReference {!r}>".format(self.name)
+        return "<ImageReference {!r}>".format(self.name)
 
     def __hash__(self):
         return hash(self.name)
@@ -215,7 +215,7 @@ class ImageReference(renpy.display.core.Displayable):
 
                         self.param_target = self.target
 
-                except Exception, e:
+                except Exception as e:
                     if renpy.config.debug:
                         raise
 
@@ -323,7 +323,7 @@ class DynamicImage(renpy.display.core.Displayable):
         return self.find_target(scope, update)
 
     def __unicode__(self):
-        return u"DynamicImage {!r}".format(self.name)
+        return "DynamicImage {!r}".format(self.name)
 
     def __hash__(self):
         return hash(self.name)
@@ -514,7 +514,7 @@ class ShownImageInfo(renpy.object.Object):
         if layer is None:
             layer = 'master'
 
-        for l, t in self.attributes.keys():
+        for l, t in list(self.attributes.keys()):
             if l == layer:
                 del self.attributes[l, t]
 
diff --git a/renpy/display/imagelike.py b/renpy/display/imagelike.py
index d8b9201..3f9a6b3 100644
--- a/renpy/display/imagelike.py
+++ b/renpy/display/imagelike.py
@@ -275,8 +275,8 @@ class Frame(renpy.display.core.Displayable):
                     newcr = Render(cdw, cdh)
                     newcr.clipping = True
 
-                    for x in xrange(0, cdw, csw):
-                        for y in xrange(0, cdh, csh):
+                    for x in range(0, cdw, csw):
+                        for y in range(0, cdh, csh):
                             newcr.blit(cr, (x, y))
 
                     cr = newcr
diff --git a/renpy/display/layout.py b/renpy/display/layout.py
index 7ded388..3b2fd9f 100644
--- a/renpy/display/layout.py
+++ b/renpy/display/layout.py
@@ -180,7 +180,7 @@ class Container(renpy.display.core.Displayable):
         if len(offsets) != len(children):
             return None
 
-        for i in xrange(len(offsets) - 1, -1, -1):
+        for i in range(len(offsets) - 1, -1, -1):
 
             d = children[i]
             xo, yo = offsets[i]
@@ -641,9 +641,9 @@ class MultiBox(Container):
             rv = None
 
             if self.style.order_reverse:
-                iterator = zip(reversed(self.children), reversed(csts), reversed(cats))
+                iterator = list(zip(reversed(self.children), reversed(csts), reversed(cats)))
             else:
-                iterator = zip(self.children, csts, cats)
+                iterator = list(zip(self.children, csts, cats))
 
             for child, cst, cat in iterator:
 
@@ -889,7 +889,7 @@ class MultiBox(Container):
 
     def event(self, ev, x, y, st):
 
-        children_offsets = zip(self.children, self.offsets, self.start_times)
+        children_offsets = list(zip(self.children, self.offsets, self.start_times))
 
         if not self.style.order_reverse:
             children_offsets.reverse()
@@ -1126,7 +1126,7 @@ class DynamicDisplayable(renpy.display.core.Displayable):
         super(DynamicDisplayable, self).__init__()
         self.child = None
 
-        if isinstance(function, basestring):
+        if isinstance(function, str):
             args = ( function, )
             kwargs = { }
             function = dynamic_displayable_compat
@@ -1669,7 +1669,7 @@ class Side(Container):
 
         super(Side, self).__init__(style=style, **properties)
 
-        if isinstance(positions, basestring):
+        if isinstance(positions, str):
             positions = positions.split()
 
         seen = set()
diff --git a/renpy/display/module.py b/renpy/display/module.py
index 24c0f7d..de7ad91 100644
--- a/renpy/display/module.py
+++ b/renpy/display/module.py
@@ -172,11 +172,11 @@ def twomap(src, dst, white, black):
                wb + 1,
                wa + 1)
     else:
-        map(src, dst,
+        list(map(src, dst,
             ramp(br, wr),
             ramp(bg, wg),
             ramp(bb, wb),
-            ramp(0, wa))
+            ramp(0, wa)))
 
 
 def alpha_munge(src, dst, amap):
diff --git a/renpy/display/motion.py b/renpy/display/motion.py
index c80ae9e..d24b2d8 100644
--- a/renpy/display/motion.py
+++ b/renpy/display/motion.py
@@ -542,7 +542,7 @@ class Transform(Container):
             self.arguments = { }
 
             # Fill self.arguments with a
-            for k, v in kwargs.iteritems():
+            for k, v in kwargs.items():
 
                 prefix = ""
                 prop = k
@@ -568,7 +568,7 @@ class Transform(Container):
                         prefix = new_prefix
 
             if "" in self.arguments:
-                for k, v in self.arguments[""].iteritems():
+                for k, v in self.arguments[""].items():
                     setattr(self.state, k, v)
 
         else:
@@ -627,7 +627,7 @@ class Transform(Container):
             if d is None:
                 continue
 
-            for k, v in d.iteritems():
+            for k, v in d.items():
                 setattr(state, k, v)
 
         return None
@@ -803,7 +803,7 @@ class Transform(Container):
         if not offsets:
             return None
 
-        for i in xrange(len(self.children)-1, -1, -1):
+        for i in range(len(self.children)-1, -1, -1):
 
             d = children[i]
             xo, yo = offsets[i]
@@ -1221,10 +1221,11 @@ class Revolver(object):
         self.pos = pos
         self.child = child
 
-    def __call__(self, t, (w, h, cw, ch)):
+    def __call__(self, t, xxx_todo_changeme):
 
         # Converts a float to an integer in the given range, passes
         # integers through unchanged.
+        (w, h, cw, ch) = xxx_todo_changeme
         def fti(x, r):
             if x is None:
                 x = 0
diff --git a/renpy/display/particle.py b/renpy/display/particle.py
index f821dc8..959fce1 100644
--- a/renpy/display/particle.py
+++ b/renpy/display/particle.py
@@ -295,7 +295,7 @@ class SpriteManager(renpy.display.core.Displayable):
         return rv
 
     def event(self, ev, x, y, st):
-        for i in xrange(len(self.children) -1, -1, -1):
+        for i in range(len(self.children) -1, -1, -1):
             s = self.children[i]
 
             if s.events:
@@ -432,7 +432,7 @@ class SnowBlossomFactory(renpy.python.NoRollback):
         self.init()
 
     def init(self):
-        self.starts = [ random.uniform(0, self.start) for _i in xrange(0, self.count) ] # W0201
+        self.starts = [ random.uniform(0, self.start) for _i in range(0, self.count) ] # W0201
         self.starts.append(self.start)
         self.starts.sort()
 
@@ -447,7 +447,7 @@ class SnowBlossomFactory(renpy.python.NoRollback):
         if (st == 0) and not particles and self.fast:
             rv = [ ]
 
-            for _i in xrange(0, self.count):
+            for _i in range(0, self.count):
                 rv.append(SnowBlossomParticle(self.image,
                                               ranged(self.xspeed),
                                               ranged(self.yspeed),
diff --git a/renpy/display/pgrender.py b/renpy/display/pgrender.py
index 9418095..c86000e 100644
--- a/renpy/display/pgrender.py
+++ b/renpy/display/pgrender.py
@@ -87,14 +87,14 @@ class Surface(pygame.Surface):
         rv = pygame.Surface.subsurface(self, rect)
         return rv
 
-def surface((width, height), alpha):
+def surface(xxx_todo_changeme, alpha):
     """
     Constructs a new surface. The allocated surface is actually a subsurface
     of a surface that has a 2 pixel border in all directions.
 
     `alpha` - True if the new surface should have an alpha channel.
     """
-
+    (width, height) = xxx_todo_changeme
     if isinstance(alpha, pygame.Surface):
         alpha = alpha.get_masks()[3]
 
diff --git a/renpy/display/predict.py b/renpy/display/predict.py
index 2b37a9f..305e0b5 100644
--- a/renpy/display/predict.py
+++ b/renpy/display/predict.py
@@ -120,7 +120,7 @@ def prediction_coroutine(root_widget):
     if len(renpy.game.contexts) >= 2:
         sls = renpy.game.contexts[-2].scene_lists
 
-        for l in sls.layers.itervalues():
+        for l in sls.layers.values():
             for sle in l:
                 try:
                     displayable(sle.displayable)
@@ -138,7 +138,7 @@ def prediction_coroutine(root_widget):
 
 
     # Predict screens given with renpy.start_predict_screen.
-    for name, value in renpy.store._predict_screen.items():
+    for name, value in list(renpy.store._predict_screen.items()):
         args, kwargs = value
 
         renpy.display.screen.predict_screen(name, *args, **kwargs)
diff --git a/renpy/display/screen.py b/renpy/display/screen.py
index 585c6bf..622ba60 100644
--- a/renpy/display/screen.py
+++ b/renpy/display/screen.py
@@ -103,7 +103,7 @@ class ScreenProfile(renpy.object.Object):
         self.const = const
 
         if name is not None:
-            if isinstance(name, basestring):
+            if isinstance(name, str):
                 name = tuple(name.split())
                 profile[name] = self
 
@@ -116,7 +116,7 @@ def get_profile(name):
         A string or tuple.
     """
 
-    if isinstance(name, basestring):
+    if isinstance(name, str):
         name = tuple(name.split())
 
     if name in profile:
@@ -202,7 +202,7 @@ class Screen(renpy.object.Object):
                  location=None):
 
         # The name of this screen.
-        if isinstance(name, basestring):
+        if isinstance(name, str):
             name = tuple(name.split())
 
         self.name = name
@@ -700,7 +700,7 @@ def get_all_screen_variants(name):
 
     rv = [ ]
 
-    for k, v in screens.iteritems():
+    for k, v in screens.items():
         if k[0] == name:
             rv.append((k[1], v))
 
@@ -733,7 +733,7 @@ def sort_screens():
     # For each screen, the set of screens that use it.
     reverse = collections.defaultdict(set)
 
-    for k, v in screens.items():
+    for k, v in list(screens.items()):
 
         name = k[0]
 
@@ -751,7 +751,7 @@ def sort_screens():
 
     rv = [ ]
 
-    workset = { k for k, v in depends.items() if not len(v) }
+    workset = { k for k, v in list(depends.items()) if not len(v) }
 
     while workset:
         name = workset.pop()
@@ -782,7 +782,7 @@ def sorted_variants():
     rv = [ ]
 
     for name in sort_screens():
-        rv.extend(screens_by_name[name].values())
+        rv.extend(list(screens_by_name[name].values()))
 
     return rv
 
@@ -892,7 +892,7 @@ def get_screen(name, layer="screens"):
 
     """
 
-    if isinstance(name, basestring):
+    if isinstance(name, str):
         name = (name, )
 
     sl = renpy.exports.scene_lists()
@@ -1057,7 +1057,7 @@ def predict_screen(_screen_name, *_args, **kwargs):
         if renpy.config.debug_image_cache:
             import traceback
 
-            print "While predicting screen", _screen_name
+            print("While predicting screen", _screen_name)
             traceback.print_exc()
 
     renpy.ui.reset()
@@ -1175,7 +1175,7 @@ def before_restart():
     longer defined.
     """
 
-    for k, layer in renpy.display.interface.old_scene.iteritems():
+    for k, layer in renpy.display.interface.old_scene.items():
         if k is None:
             continue
 
diff --git a/renpy/display/swdraw.py b/renpy/display/swdraw.py
index 1f620f1..a90e524 100644
--- a/renpy/display/swdraw.py
+++ b/renpy/display/swdraw.py
@@ -303,7 +303,7 @@ def draw_special(what, dest, x, y):
 
         ramp = "\x00" * 256
 
-        for i in xrange(0, ramplen):
+        for i in range(0, ramplen):
             ramp += chr(255 * i / ramplen)
 
         ramp += "\xff" * 256
diff --git a/renpy/dump.py b/renpy/dump.py
index f047146..c01a960 100644
--- a/renpy/dump.py
+++ b/renpy/dump.py
@@ -113,14 +113,14 @@ def dump(error):
     # Labels.
     label = location["label"] = { }
 
-    for name, n in renpy.game.script.namemap.iteritems():
+    for name, n in renpy.game.script.namemap.items():
         filename = n.filename
         line = n.linenumber
 
-        if not isinstance(name, basestring):
+        if not isinstance(name, str):
             continue
 
-        if not filter(name, filename):
+        if not list(filter(name, filename)):
             continue
 
         label[name] = [ filename, line ]
@@ -130,7 +130,7 @@ def dump(error):
     define = location["define"] = { }
 
     for name, filename, line in definitions:
-        if not filter(name, filename):
+        if not list(filter(name, filename)):
             continue
 
         define[name] = [ filename, line ]
@@ -139,7 +139,7 @@ def dump(error):
     screen = location["screen"] = { }
 
     for name, filename, line in screens:
-        if not filter(name, filename):
+        if not list(filter(name, filename)):
             continue
 
         screen[name] = [ filename, line ]
@@ -148,7 +148,7 @@ def dump(error):
     transform = location["transform"] = { }
 
     for name, filename, line in transforms:
-        if not filter(name, filename):
+        if not list(filter(name, filename)):
             continue
 
         transform[name] = [ filename, line ]
@@ -166,16 +166,16 @@ def dump(error):
         """
 
         if inspect.isfunction(o):
-            return inspect.getfile(o), o.func_code.co_firstlineno
+            return inspect.getfile(o), o.__code__.co_firstlineno
 
         if inspect.ismethod(o):
-            return get_line(o.im_func)
+            return get_line(o.__func__)
 
         return None, None
 
     code = location["callable"] = { }
 
-    for modname, mod in sys.modules.items():
+    for modname, mod in list(sys.modules.items()):
 
         if mod is None:
             continue
@@ -187,7 +187,7 @@ def dump(error):
         else:
             continue
 
-        for name, o in mod.__dict__.items():
+        for name, o in list(mod.__dict__.items()):
 
             if inspect.isfunction(o):
                 try:
@@ -199,7 +199,7 @@ def dump(error):
                     if filename is None:
                         continue
 
-                    if not filter(name, filename):
+                    if not list(filter(name, filename)):
                         continue
 
                     code[prefix + name] = [ filename, line ]
@@ -208,7 +208,7 @@ def dump(error):
 
             if inspect.isclass(o):
 
-                for methname, method in o.__dict__.iteritems():
+                for methname, method in o.__dict__.items():
 
                     try:
                         if inspect.getmodule(method) != mod:
@@ -219,10 +219,10 @@ def dump(error):
                         if filename is None:
                             continue
 
-                        if not filter(name, filename):
+                        if not list(filter(name, filename)):
                             continue
 
-                        if not filter(methname, filename):
+                        if not list(filter(methname, filename)):
                             continue
 
                         code[prefix + name + "." + methname] = [ filename, line ]
@@ -236,7 +236,7 @@ def dump(error):
         pass
 
     if args.json_dump != "-":
-        with file(args.json_dump, "w") as f:
+        with open(args.json_dump, "w") as f:
             json.dump(result, f)
     else:
         json.dump(result, sys.stdout, indent=2)
diff --git a/renpy/easy.py b/renpy/easy.py
index 6aacea1..85f3d17 100644
--- a/renpy/easy.py
+++ b/renpy/easy.py
@@ -36,7 +36,7 @@ def displayable_or_none(d, scope=None):
     if d is None:
         return d
 
-    if isinstance(d, basestring):
+    if isinstance(d, str):
         if not d:
             raise Exception("An empty string cannot be used as a displayable.")
         elif ("[" in d) and renpy.config.dynamic_images:
@@ -70,7 +70,7 @@ def displayable(d, scope=None):
     if isinstance(d, renpy.display.core.Displayable):
         return d
 
-    if isinstance(d, basestring):
+    if isinstance(d, str):
         if not d:
             raise Exception("An empty string cannot be used as a displayable.")
         elif ("[" in d) and renpy.config.dynamic_images:
@@ -97,7 +97,7 @@ def dynamic_image(d, scope=None):
     Substitutes a scope into `d`, then returns a displayable.
     """
 
-    if isinstance(d, basestring):
+    if isinstance(d, str):
         d = renpy.substitutions.substitute(d, scope=scope, force=True, translate=False)[0]
 
     return displayable_or_none(d)
@@ -114,7 +114,7 @@ def predict(d):
 def timed(name):
     start = time.time()
     yield
-    print "{0}: {1:.2f} ms".format(name, (time.time() - start) * 1000.0)
+    print("{0}: {1:.2f} ms".format(name, (time.time() - start) * 1000.0))
 
 
 def split_properties(properties, *prefixes):
@@ -145,7 +145,7 @@ def split_properties(properties, *prefixes):
 
     prefix_d = list(zip(prefixes, rv))
 
-    for k, v in properties.iteritems():
+    for k, v in properties.items():
         for prefix, d in prefix_d:
             if k.startswith(prefix):
                 d[k[len(prefix):]] = v
diff --git a/renpy/editor.py b/renpy/editor.py
index 7be4c32..bac80f7 100644
--- a/renpy/editor.py
+++ b/renpy/editor.py
@@ -114,7 +114,7 @@ def init():
         return
 
     scope = { "__file__" : path }
-    execfile(path, scope, scope)
+    exec(compile(open(path).read(), path, 'exec'), scope, scope)
 
     if "Editor" in scope:
         editor = scope["Editor"]()
diff --git a/renpy/error.py b/renpy/error.py
index d70fe2a..513cd73 100644
--- a/renpy/error.py
+++ b/renpy/error.py
@@ -23,7 +23,7 @@
 
 import traceback
 import sys
-import cStringIO
+import io
 import platform
 import linecache
 
@@ -43,8 +43,8 @@ def write_utf8_traceback_list(out, l):
     for filename, line, what, text in l:
 
         # Filename is either unicode or an fsecoded string.
-        if not isinstance(filename, unicode):
-            filename = unicode(filename, FSENCODING, "replace")
+        if not isinstance(filename, str):
+            filename = str(filename, FSENCODING, "replace")
 
         # Line is a number.
 
@@ -52,12 +52,12 @@ def write_utf8_traceback_list(out, l):
         # or comes from inside Ren'Py.
 
         if isinstance(text, str):
-            text = text.decode("utf-8", "replace")
+            text = text.encode(errors="replace").decode("utf-8", "replace")
 
         ul.append((filename, line, what, text))
 
     for t in traceback.format_list(ul):
-        out.write(t.encode("utf-8", "replace"))
+        out.write(str(t.encode("utf-8", "replace"), errors="replace"))
 
 
 def traceback_list(tb):
@@ -127,13 +127,13 @@ def open_error_file(fn, mode):
 
     try:
         new_fn = os.path.join(renpy.config.logdir, fn)
-        f = file(new_fn, mode)
+        f = open(new_fn, mode)
         return f, new_fn
     except:
         pass
 
     try:
-        f = file(fn, mode)
+        f = open(fn, mode)
         return f, fn
     except:
         pass
@@ -141,7 +141,7 @@ def open_error_file(fn, mode):
     import tempfile
 
     new_fn = os.path.join(tempfile.gettempdir(), "renpy-" + fn)
-    return file(new_fn, mode), new_fn
+    return open(new_fn, mode), new_fn
 
 def report_exception(e, editor=True):
     """
@@ -157,11 +157,11 @@ def report_exception(e, editor=True):
 
     type, _value, tb = sys.exc_info() #@ReservedAssignment
 
-    print(repr(e))
+    print((repr(e)))
 
     def safe_utf8(e):
         try:
-            m = unicode(e)
+            m = str(e)
         except:
             try:
                 if len(e.args) == 0:
@@ -176,27 +176,27 @@ def report_exception(e, editor=True):
                 except:
                     m = "<Could not encode exception.>"
 
-        if isinstance(m, unicode):
+        if isinstance(m, str):
             return m.encode("utf-8", "replace")
         else:
             return m
 
     # Return values - which can be displayed to the user.
-    simple = cStringIO.StringIO()
-    full = cStringIO.StringIO()
+    simple = io.StringIO()
+    full = io.StringIO()
 
     full_tl = traceback_list(tb)
     simple_tl = filter_traceback_list(full_tl)
 
-    print >>simple, renpy.game.exception_info
+    print(renpy.game.exception_info, file=simple)
     write_utf8_traceback_list(simple, simple_tl)
-    print >>simple, type.__name__ + ":",
-    print >>simple, safe_utf8(e)
+    print(type.__name__ + ":", end=' ', file=simple)
+    print(safe_utf8(e), file=simple)
 
-    print >>full, "Full traceback:"
+    print("Full traceback:", file=full)
     write_utf8_traceback_list(full, full_tl)
-    print >>full, type.__name__ + ":",
-    print >>full, safe_utf8(e)
+    print(type.__name__ + ":", end=' ', file=full)
+    print(safe_utf8(e), file=full)
 
     # Write to stdout/stderr.
     sys.stdout.write("\n")
@@ -204,11 +204,11 @@ def report_exception(e, editor=True):
     sys.stdout.write("\n")
     sys.stdout.write(simple.getvalue())
 
-    print >>full
+    print(file=full)
     try:
-        print >>full, platform.platform()
-        print >>full, renpy.version
-        print >>full, renpy.config.name + " " + renpy.config.version
+        print(platform.platform(), file=full)
+        print(renpy.version, file=full)
+        print(renpy.config.name + " " + renpy.config.version, file=full)
     except:
         pass
 
@@ -223,14 +223,14 @@ def report_exception(e, editor=True):
 
         f.write(codecs.BOM_UTF8)
 
-        print >>f, "I'm sorry, but an uncaught exception occurred."
-        print >>f
+        print("I'm sorry, but an uncaught exception occurred.", file=f)
+        print(file=f)
 
         f.write(simple)
 
-        print >>f
-        print >>f, "-- Full Traceback ------------------------------------------------------------"
-        print >>f
+        print(file=f)
+        print("-- Full Traceback ------------------------------------------------------------", file=f)
+        print(file=f)
 
         f.write(full)
         f.close()
@@ -249,6 +249,6 @@ def report_exception(e, editor=True):
     except:
         pass
 
-    return simple.decode("utf-8", "replace"), full.decode("utf-8", "replace"), traceback_fn
+    return simple.encode(errors="replace").decode("utf-8", "replace"), full.encode(errors="replace").decode("utf-8", "replace"), traceback_fn
 
 
diff --git a/renpy/execution.py b/renpy/execution.py
index 224c631..a41b59a 100644
--- a/renpy/execution.py
+++ b/renpy/execution.py
@@ -210,7 +210,7 @@ class Context(renpy.object.Object):
 
             vars(self.info).update(vars(context.info))
 
-            for k, v in context.music.iteritems():
+            for k, v in context.music.items():
                 self.music[k] = v.copy()
 
             self.images = renpy.display.image.ShownImageInfo(context.images)
@@ -279,7 +279,7 @@ class Context(renpy.object.Object):
 
         dynamic = self.dynamic_stack.pop()
 
-        for k, v in dynamic.iteritems():
+        for k, v in dynamic.items():
             if isinstance(v, Delete):
                 del store[k]
             else:
@@ -390,14 +390,14 @@ class Context(renpy.object.Object):
                     if developer and self.next_node:
                         self.check_stacks()
 
-                except renpy.game.CONTROL_EXCEPTIONS, e:
+                except renpy.game.CONTROL_EXCEPTIONS as e:
 
                     # An exception ends the current translation.
                     self.translate_interaction = None
 
                     raise
 
-                except Exception, e:
+                except Exception as e:
                     self.translate_interaction = None
 
                     exc_info = sys.exc_info()
@@ -408,18 +408,18 @@ class Context(renpy.object.Object):
                             self.exception_handler(short, full, traceback_fn)
                         elif renpy.display.error.report_exception(short, full, traceback_fn):
                             raise
-                    except renpy.game.CONTROL_EXCEPTIONS, ce:
+                    except renpy.game.CONTROL_EXCEPTIONS as ce:
                         raise ce
-                    except Exception, ce:
-                        raise exc_info[0], exc_info[1], exc_info[2]
+                    except Exception as ce:
+                        raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
 
                 node = self.next_node
 
-            except renpy.game.JumpException, e:
+            except renpy.game.JumpException as e:
                 node = renpy.game.script.lookup(e.args[0])
                 self.abnormal = True
 
-            except renpy.game.CallException, e:
+            except renpy.game.CallException as e:
 
                 if self.next_node is None:
                     raise Exception("renpy.call can't be used when the next node is undefined.")
@@ -495,7 +495,7 @@ class Context(renpy.object.Object):
             if renpy.game.script.has_label(self.return_stack[-1]):
                 node = renpy.game.script.lookup(self.return_stack[-1])
             elif renpy.game.script.has_label(self.call_location_stack[-1]):
-                node = renpy.game.script.lookup(self.call_location_stack[-1]).next
+                node = renpy.game.script.lookup(self.call_location_stack[-1]).__next__
 
             if node is None:
 
@@ -629,9 +629,9 @@ class Context(renpy.object.Object):
                 if renpy.config.debug_image_cache:
                     import traceback
 
-                    print
+                    print()
                     traceback.print_exc()
-                    print "While predicting images."
+                    print("While predicting images.")
 
             self.images = old_images
             self.predict_return_stack = None
diff --git a/renpy/exports.py b/renpy/exports.py
index faccd36..82bc282 100644
--- a/renpy/exports.py
+++ b/renpy/exports.py
@@ -25,7 +25,7 @@
 # alone as part of the api.
 
 # Remember the real file.
-_file = file
+_file = open
 
 import renpy.display
 import renpy.audio
@@ -39,7 +39,7 @@ def renpy_pure(fn):
 
     name = fn
 
-    if not isinstance(name, basestring):
+    if not isinstance(name, str):
         name = fn.__name__
 
     pure("renpy." + name)
@@ -371,7 +371,7 @@ def copy_images(old, new):
 
     lenold = len(old)
 
-    for k, v in renpy.display.image.images.items():
+    for k, v in list(renpy.display.image.images.items()):
         if len(k) < lenold:
             continue
 
@@ -495,7 +495,7 @@ def predict_show(name, layer=None, what=None, tag=None, at_list=[ ]):
 
     if what is None:
         what = name
-    elif isinstance(what, basestring):
+    elif isinstance(what, str):
         what = tuple(what.split())
 
     if isinstance(what, renpy.display.core.Displayable):
@@ -593,7 +593,7 @@ def show(name, at_list=[ ], layer=None, what=None, zorder=None, tag=None, behind
 
     if what is None:
         what = name
-    elif isinstance(what, basestring):
+    elif isinstance(what, str):
         what = tuple(what.split())
 
     if isinstance(what, renpy.display.core.Displayable):
@@ -740,7 +740,7 @@ def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=N
     renpy.exports.mode('input')
 
     roll_forward = renpy.exports.roll_forward_info()
-    if not isinstance(roll_forward, basestring):
+    if not isinstance(roll_forward, str):
         roll_forward = None
 
     # use previous data in rollback
@@ -1038,7 +1038,7 @@ class TagQuotingDict(object):
         if key in store:
             rv = store[key]
 
-            if isinstance(rv, (str, unicode)):
+            if isinstance(rv, str):
                 rv = rv.replace("{", "{{")
 
             return rv
@@ -1060,7 +1060,7 @@ def predict_say(who, what):
     if who is None:
         who = renpy.store.narrator # E1101 @UndefinedVariable
 
-    if isinstance(who, (str, unicode)):
+    if isinstance(who, str):
         return renpy.store.predict_say(who, what)
 
     predict = getattr(who, 'predict', None)
@@ -1115,7 +1115,7 @@ def say(who, what, interact=True):
     if who is None:
         who = renpy.store.narrator # E1101 @UndefinedVariable
 
-    if isinstance(who, (str, unicode)):
+    if isinstance(who, str):
         renpy.store.say(who, what, interact=interact)
     else:
         who(what, interact=interact)
@@ -1444,8 +1444,8 @@ def get_all_labels():
     """
     rv = [ ]
 
-    for i in renpy.game.script.namemap.iterkeys():
-        if isinstance(i, basestring):
+    for i in renpy.game.script.namemap.keys():
+        if isinstance(i, str):
             rv.append(i)
 
     return renpy.python.RevertableSet(rv)
@@ -1792,7 +1792,7 @@ def log(msg):
 
         import textwrap
 
-        print >>logfile, textwrap.fill(msg, renpy.config.log_width).encode("utf-8")
+        print(textwrap.fill(msg, renpy.config.log_width).encode("utf-8"), file=logfile)
         logfile.flush()
 
     except:
@@ -1908,7 +1908,7 @@ def seen_image(name):
     return name in renpy.game.persistent._seen_images  # @UndefinedVariable
 
 
-def file(fn): #@ReservedAssignment
+def open(fn): #@ReservedAssignment
     """
     :doc: file
 
@@ -1958,7 +1958,7 @@ def get_at_list(name, layer=None):
     If `layer` is None, uses the default layer for the given tag.
     """
 
-    if isinstance(name, basestring):
+    if isinstance(name, str):
         name = tuple(name.split())
 
     tag = name[0]
@@ -2173,7 +2173,7 @@ def load_string(s, filename="<string>"):
         old_locked = renpy.config.locked
         renpy.config.locked = False
 
-        stmts, initcode = renpy.game.script.load_string(filename, unicode(s))
+        stmts, initcode = renpy.game.script.load_string(filename, str(s))
 
         if stmts is None:
             return None
@@ -2459,7 +2459,7 @@ def call_screen(_screen_name, *args, **kwargs):
 
     try:
         rv = renpy.ui.interact(mouse="screen", type="screen", roll_forward=roll_forward)
-    except (renpy.game.JumpException, renpy.game.CallException), e:
+    except (renpy.game.JumpException, renpy.game.CallException) as e:
         rv = e
 
     renpy.exports.checkpoint(rv)
@@ -2616,7 +2616,7 @@ def variant(name):
     returns True if any of the variants is selected.
     """
 
-    if isinstance(name, basestring):
+    if isinstance(name, str):
         return name in renpy.config.variants
     else:
         for n in name:
@@ -2745,7 +2745,7 @@ def fsencode(s):
     Converts s from unicode to the filesystem encoding.
     """
 
-    if not isinstance(s, unicode):
+    if not isinstance(s, str):
         return s
 
     fsencoding = sys.getfilesystemencoding() or "utf-8"
diff --git a/renpy/game.py b/renpy/game.py
index d66800c..1ba0fe5 100644
--- a/renpy/game.py
+++ b/renpy/game.py
@@ -264,7 +264,7 @@ def invoke_in_new_context(callable, *args, **kwargs): #@ReservedAssignment
 
         return callable(*args, **kwargs)
 
-    except renpy.game.JumpOutException, e:
+    except renpy.game.JumpOutException as e:
 
         raise renpy.game.JumpException(e.args[0])
 
@@ -312,7 +312,7 @@ def call_in_new_context(label, *args, **kwargs):
         context.goto_label(label)
         return renpy.execution.run_context(False)
 
-    except renpy.game.JumpOutException, e:
+    except renpy.game.JumpOutException as e:
 
         raise renpy.game.JumpException(e.args[0])
 
@@ -350,7 +350,7 @@ def call_replay(label, scope={}):
 
     renpy.exports.execute_default_statement(True)
 
-    for k, v in scope.iteritems():
+    for k, v in scope.items():
         setattr(renpy.store, k, v)
 
     renpy.store._in_replay = label
diff --git a/renpy/lint.py b/renpy/lint.py
index 088b89c..b6f131c 100644
--- a/renpy/lint.py
+++ b/renpy/lint.py
@@ -28,9 +28,9 @@ import sys
 import collections
 import textwrap
 
-import __builtin__
+import builtins
 
-python_builtins = set(dir(__builtin__))
+python_builtins = set(dir(builtins))
 renpy_builtins = set()
 
 image_prefixes = None
@@ -53,13 +53,13 @@ report_node = None
 # Reports a message to the user.
 def report(msg, *args):
     if report_node:
-        out = u"%s:%d " % (renpy.parser.unicode_filename(report_node.filename), report_node.linenumber)
+        out = "%s:%d " % (renpy.parser.unicode_filename(report_node.filename), report_node.linenumber)
     else:
         out = ""
 
     out += msg % args
-    print
-    print out.encode('utf-8')
+    print()
+    print(out.encode('utf-8'))
 
 added = { }
 
@@ -68,7 +68,7 @@ added = { }
 def add(msg):
     if not msg in added:
         added[msg] = True
-        print unicode(msg).encode('utf-8')
+        print(str(msg).encode('utf-8'))
 
 
 # Trys to evaluate an expression, announcing an error if it fails.
@@ -473,7 +473,7 @@ def check_define(node, kind):
 def check_style(name, s):
 
     for p in s.properties:
-        for k, v in p.iteritems():
+        for k, v in p.items():
 
             kname = name + ", property " + k
 
@@ -504,7 +504,7 @@ def check_label(node):
 
 
 def check_styles():
-    for full_name, s in renpy.style.styles.iteritems(): # @UndefinedVariable
+    for full_name, s in renpy.style.styles.items(): # @UndefinedVariable
         name = "style." + full_name[0]
         for i in full_name[1:]:
             name += "[{!r}]".format(i)
@@ -588,8 +588,8 @@ def lint():
 
     renpy.game.lint = True
 
-    print codecs.BOM_UTF8
-    print unicode(renpy.version + " lint report, generated at: " + time.ctime()).encode("utf-8")
+    print(codecs.BOM_UTF8)
+    print(str(renpy.version + " lint report, generated at: " + time.ctime()).encode("utf-8"))
 
     # This supports check_hide.
     global image_prefixes
@@ -723,10 +723,10 @@ characters per block. """.format(
         lines.append(s)
 
 
-    print
-    print
-    print "Statistics:"
-    print
+    print()
+    print()
+    print("Statistics:")
+    print()
 
     languages = list(counts)
     languages.sort()
@@ -738,17 +738,17 @@ characters per block. """.format(
 
     for l in lines:
         for ll in textwrap.wrap(l, 78):
-            print ll.encode("utf-8")
+            print(ll.encode("utf-8"))
 
-        print
+        print()
 
-    print
+    print()
     if renpy.config.developer:
-        print "Remember to set config.developer to False before releasing."
-        print
+        print("Remember to set config.developer to False before releasing.")
+        print()
 
-    print "Lint is not a substitute for thorough testing. Remember to update Ren'Py"
-    print "before releasing. New releases fix bugs and improve compatibility."
+    print("Lint is not a substitute for thorough testing. Remember to update Ren'Py")
+    print("before releasing. New releases fix bugs and improve compatibility.")
 
     return False
 
diff --git a/renpy/loader.py b/renpy/loader.py
index 0760219..f3a7a7b 100644
--- a/renpy/loader.py
+++ b/renpy/loader.py
@@ -22,7 +22,7 @@
 import renpy
 import os.path
 from pickle import loads
-from cStringIO import StringIO
+from io import StringIO
 import sys
 import types
 import threading
@@ -30,7 +30,7 @@ import zlib
 
 # Ensure the utf-8 codec is loaded, to prevent recursion when we use it
 # to look up filenames.
-u"".encode("utf-8")
+"".encode("utf-8")
 
 
 ################################################################# Physical Paths
@@ -61,7 +61,7 @@ try:
 
     expansion = os.environ.get("ANDROID_EXPANSION", None)
     if expansion is not None:
-        print "Using expansion file", expansion
+        print("Using expansion file", expansion)
 
         apks = [
             android.apk.APK(apk=expansion, prefix='assets/x-game/'),
@@ -71,7 +71,7 @@ try:
         game_apks = [ apks[0] ]
 
     else:
-        print "Not using expansion file."
+        print("Not using expansion file.")
 
         apks = [
             android.apk.APK(prefix='assets/x-game/'),
@@ -121,7 +121,7 @@ def index_archives():
 
         try:
             fn = transfn(prefix + ".rpa")
-            f = file(fn, "rb")
+            f = open(fn, "rb")
             l = f.readline()
 
             # 3.0 Branch.
@@ -133,7 +133,7 @@ def index_archives():
 
                 # Deobfuscate the index.
 
-                for k in index.keys():
+                for k in list(index.keys()):
 
                     if len(index[k][0]) == 2:
                         index[k] = [ (offset ^ key, dlen ^ key) for offset, dlen in index[k] ]
@@ -158,7 +158,7 @@ def index_archives():
             f.close()
 
             fn = transfn(prefix + ".rpi")
-            index = loads(file(fn, "rb").read().decode("zlib"))
+            index = loads(open(fn, "rb").read().decode("zlib"))
             archives.append((prefix, index))
         except:
             raise
@@ -253,7 +253,7 @@ def scandirfiles():
     files = game_files
 
     for _prefix, index in archives:
-        for j in index.iterkeys():
+        for j in index.keys():
             add(None, j)
 
 
@@ -380,7 +380,7 @@ class SubFile(object):
     def __iter__(self):
         return self
 
-    def next(self): #@ReservedAssignment
+    def __next__(self): #@ReservedAssignment
         rv = self.readline()
 
         if not rv:
@@ -489,7 +489,7 @@ def load_core(name):
 
         # Compatibility path.
         else:
-            f = file(afn, "rb")
+            f = open(afn, "rb")
 
             for offset, dlen in index[name]:
                 f.seek(offset)
@@ -684,13 +684,13 @@ class RenpyImporter(object):
             mod.__path__ = [ filename[:-len("__init__.py")] ]
 
         source = load(filename).read().decode("utf8")
-        if source and source[0] == u'\ufeff':
+        if source and source[0] == '\ufeff':
             source = source[1:]
         source = source.encode("raw_unicode_escape")
 
         source = source.replace("\r", "")
         code = compile(source, filename, 'exec')
-        exec code in mod.__dict__
+        exec(code, mod.__dict__)
         return mod
 
     def get_data(self, filename):
@@ -773,7 +773,7 @@ def auto_thread_function():
             if auto_quit_flag:
                 return
 
-            items = auto_mtimes.items()
+            items = list(auto_mtimes.items())
 
         for fn, mtime in items:
 
diff --git a/renpy/loadsave.py b/renpy/loadsave.py
index 862105d..07fd357 100644
--- a/renpy/loadsave.py
+++ b/renpy/loadsave.py
@@ -22,9 +22,9 @@
 # This file contains functions that load and save the game state.
 
 import pickle
-import cPickle
+import pickle
 
-from cStringIO import StringIO
+from io import StringIO
 
 import zipfile
 import re
@@ -40,13 +40,13 @@ from json import dumps as json_dumps
 # Dump that chooses which pickle to use:
 def dump(o, f):
     if renpy.config.use_cpickle:
-        cPickle.dump(o, f, cPickle.HIGHEST_PROTOCOL)
+        pickle.dump(o, f, pickle.HIGHEST_PROTOCOL)
     else:
         pickle.dump(o, f, pickle.HIGHEST_PROTOCOL)
 
 def loads(s):
     if renpy.config.use_cpickle:
-        return cPickle.loads(s)
+        return pickle.loads(s)
     else:
         return pickle.loads(s)
 
@@ -71,10 +71,10 @@ def save_dump(roots, log):
             f.write("{0: 7d} {1} = alias {2}\n".format(0, path, o_repr_cache[ido]))
             return 0
 
-        if isinstance(o, (int, float, types.NoneType, types.ModuleType, types.ClassType)):
+        if isinstance(o, (int, float, type(None), types.ModuleType, type)):
             o_repr = repr(o)
 
-        elif isinstance(o, (str, unicode)):
+        elif isinstance(o, str):
             if len(o) <= 80:
                 o_repr = repr(o).encode("utf-8")
             else:
@@ -87,7 +87,7 @@ def save_dump(roots, log):
             o_repr = "<" + o.__class__.__name__ + ">"
 
         elif isinstance(o, types.MethodType):
-            o_repr = "<method {0}.{1}>".format(o.im_class.__name__, o.im_func.__name__)
+            o_repr = "<method {0}.{1}>".format(o.__self__.__class__.__name__, o.__func__.__name__)
 
         elif isinstance(o, object):
             o_repr = "<{0}>".format(type(o).__name__)
@@ -98,10 +98,10 @@ def save_dump(roots, log):
 
         o_repr_cache[ido] = o_repr
 
-        if isinstance(o, (int, float, types.NoneType, types.ModuleType, types.ClassType)):
+        if isinstance(o, (int, float, type(None), types.ModuleType, type)):
             size = 1
 
-        elif isinstance(o, (str, unicode)):
+        elif isinstance(o, str):
             size = len(o) / 40 + 1
 
         elif isinstance(o, (tuple, list)):
@@ -112,12 +112,12 @@ def save_dump(roots, log):
 
         elif isinstance(o, dict):
             size = 2
-            for k, v in o.iteritems():
+            for k, v in o.items():
                 size += 2
                 size += visit(v, "{0}[{1!r}]".format(path, k))
 
         elif isinstance(o, types.MethodType):
-            size = 1 + visit(o.im_self, path + ".im_self")
+            size = 1 + visit(o.__self__, path + ".im_self")
 
         else:
 
@@ -141,7 +141,7 @@ def save_dump(roots, log):
 
             state = get(2, { })
             if isinstance(state, dict):
-                for k, v in state.iteritems():
+                for k, v in state.items():
                     size += 2
                     size += visit(v, path + "." + k)
             else:
@@ -166,7 +166,7 @@ def save_dump(roots, log):
 
         return size
 
-    f = file("save_dump.txt", "w")
+    f = open("save_dump.txt", "w")
 
     visit(roots, "roots")
     visit(log, "log")
@@ -712,7 +712,7 @@ def clear_cache():
     Clears the entire cache.
     """
 
-    for c in cache.values():
+    for c in list(cache.values()):
         c.clear()
 
     newest_slot_cache.clear()
diff --git a/renpy/log.py b/renpy/log.py
index 87f349e..8f34999 100644
--- a/renpy/log.py
+++ b/renpy/log.py
@@ -86,7 +86,7 @@ class LogFile(object):
             altfn = os.path.join(tempfile.gettempdir(), "renpy-" + self.name + ".txt")
 
             if renpy.android:
-                print "Logging to", fn
+                print("Logging to", fn)
 
             if self.append:
                 mode = "a"
@@ -132,7 +132,7 @@ class LogFile(object):
                 s = s % args
                 s += "\n"
 
-            if not isinstance(s, unicode):
+            if not isinstance(s, str):
                 s = s.decode("latin-1")
 
             s = s.replace("\n", "\r\n")
diff --git a/renpy/main.py b/renpy/main.py
index f8bee19..721eecc 100644
--- a/renpy/main.py
+++ b/renpy/main.py
@@ -39,7 +39,7 @@ def log_clock(s):
 
     renpy.display.log.write(s)
     if renpy.android and not renpy.config.log_to_stdout:
-        print s
+        print(s)
 
     last_clock = now
 
@@ -146,7 +146,7 @@ def load_rpe(fn):
     zfn.close()
 
     sys.path.insert(0, fn)
-    exec autorun in dict()
+    exec(autorun, dict())
 
 def choose_variants():
 
@@ -174,10 +174,10 @@ def choose_variants():
             manufacturer = Build.MANUFACTURER
             model = Build.MODEL
 
-            print "Manufacturer", manufacturer, "model", model
+            print("Manufacturer", manufacturer, "model", model)
 
             if manufacturer == "Amazon" and model.startswith("AFT"):
-                print "Running on a Fire TV."
+                print("Running on a Fire TV.")
                 renpy.config.variants.insert(0, "firetv")
         except:
             pass
@@ -186,7 +186,7 @@ def choose_variants():
         package_manager = android.activity.getPackageManager()
 
         if package_manager.hasSystemFeature("android.hardware.type.television"):
-            print "Running on a television."
+            print("Running on a television.")
             renpy.config.variants.insert(0, "tv")
             renpy.config.variants.insert(0, "small")
             return
@@ -198,7 +198,7 @@ def choose_variants():
 
         info = renpy.display.get_info()
         diag = math.hypot(info.current_w, info.current_h) / android.get_dpi()
-        print "Screen diagonal is", diag, "inches."
+        print("Screen diagonal is", diag, "inches.")
 
         if diag >= 6:
             renpy.config.variants.insert(0, 'tablet')
@@ -216,7 +216,7 @@ def choose_variants():
 
         idiom = UIDevice.currentDevice().userInterfaceIdiom
 
-        print "iOS device idiom", idiom
+        print("iOS device idiom", idiom)
 
         # idiom 0 is iPhone, 1 is iPad. We assume any bigger idiom will
         # be tablet-like.
@@ -359,7 +359,7 @@ def main():
             renpy.game.script = renpy.script.Script()
             renpy.game.script.load_script()
 
-        print time.time() - start
+        print(time.time() - start)
         sys.exit(0)
 
     renpy.game.exception_info = 'After loading the script.'
@@ -470,7 +470,7 @@ def main():
                     restart = (renpy.config.end_game_transition, "_invoke_main_menu", "_main_menu")
                     renpy.persistent.update(True)
 
-            except game.FullRestartException, e:
+            except game.FullRestartException as e:
                 restart = e.reason
 
             finally:
diff --git a/renpy/memory.py b/renpy/memory.py
index 7751db1..c127e3f 100644
--- a/renpy/memory.py
+++ b/renpy/memory.py
@@ -83,7 +83,7 @@ def walk_memory(roots, seen=None):
     get_referents = gc.get_referents
     worklist_append = worklist.append
 
-    ignore_types = (types.ModuleType, types.ClassType, types.FunctionType)
+    ignore_types = (types.ModuleType, type, types.FunctionType)
 
     while worklist:
         name, o = worklist.pop(0)
@@ -133,7 +133,7 @@ def profile_memory_common(packages=[ "renpy", "store" ]):
         if mod_name.startswith("renpy.store"):
             continue
 
-        for name, o in mod.__dict__.items():
+        for name, o in list(mod.__dict__.items()):
             roots.append((mod_name + "." + name, o))
 
     return walk_memory(roots)
@@ -169,7 +169,7 @@ def profile_memory(fraction=1.0, minimum=0):
     write("Memory profile at " + time.ctime() + ":")
     write("")
 
-    usage = [ (v, k) for (k, v) in profile_memory_common()[0].items() ]
+    usage = [ (v, k) for (k, v) in list(profile_memory_common()[0].items()) ]
     usage.sort()
 
     # The total number of bytes allocated.
@@ -223,7 +223,7 @@ def diff_memory(update=True):
 
     diff = [ ]
 
-    for k, v in usage.iteritems():
+    for k, v in usage.items():
         diff.append((
             v - old_usage.get(k, 0),
             k))
@@ -275,8 +275,8 @@ def profile_rollback():
     # Walk the log, finding new roots and rollback information.
     for rb in log:
 
-        for store_name, store in rb.stores.iteritems():
-            for var_name, o in store.iteritems():
+        for store_name, store in rb.stores.items():
+            for var_name, o in store.items():
                 name = store_name + "." + var_name
                 id_o = id(o)
 
@@ -300,7 +300,7 @@ def profile_rollback():
 
     sizes = walk_memory(roots, seen)[0]
 
-    usage = [ (v, k) for (k, v) in sizes.iteritems() ]
+    usage = [ (v, k) for (k, v) in sizes.items() ]
     usage.sort()
 
     write("Total Bytes".rjust(13) + " " + "Per Rollback".rjust(13))
@@ -344,15 +344,15 @@ def find_parents(cls):
 
             objects.append(o)
 
-            print prefix + str(id(o)), type(o),
+            print(prefix + str(id(o)), type(o), end=' ')
 
             try:
                 if isinstance(o, dict) and "__name__" in o:
-                    print "with name", o["__name__"]
+                    print("with name", o["__name__"])
                 else:
-                    print repr(o)#[:1000]
+                    print(repr(o))#[:1000]
             except:
-                print "Bad repr."
+                print("Bad repr.")
 
             found = False
 
@@ -364,7 +364,7 @@ def find_parents(cls):
                 continue
 
             if isinstance(o, weakref.WeakKeyDictionary):
-                for k, v in o.data.items():
+                for k, v in list(o.data.items()):
                     if v is objects[-4]:
                         k = k()
                         seen.add(id(k))
@@ -390,7 +390,7 @@ def find_parents(cls):
                 break
 
             if not found:
-                print "<no parent, popping>"
+                print("<no parent, popping>")
 
             o, prefix = queue.pop()
 
@@ -399,8 +399,8 @@ def find_parents(cls):
             import random
             if random.random() < .1:
 
-                print
-                print "==================================================="
-                print
+                print()
+                print("===================================================")
+                print()
 
                 print_path(o)
diff --git a/renpy/parser.py b/renpy/parser.py
index 0b48859..1001749 100644
--- a/renpy/parser.py
+++ b/renpy/parser.py
@@ -37,7 +37,7 @@ parse_errors = [ ]
 class ParseError(Exception):
 
     def __init__(self, filename, number, msg, line=None, pos=None, first=False):
-        message = u"File \"%s\", line %d: %s" % (unicode_filename(filename), number, msg)
+        message = "File \"%s\", line %d: %s" % (unicode_filename(filename), number, msg)
 
         if line:
             lines = line.split('\n')
@@ -97,7 +97,7 @@ def unicode_filename(fn):
     Converts the supplied filename to unicode.
     """
 
-    if isinstance(fn, unicode):
+    if isinstance(fn, str):
         return fn
 
     # Windows.
@@ -200,7 +200,7 @@ def list_logical_lines(filename, filedata=None, linenumber=1):
     pos = 0
 
     # Skip the BOM, if any.
-    if len(data) and data[0] == u'\ufeff':
+    if len(data) and data[0] == '\ufeff':
         pos += 1
 
     if renpy.game.context().init_phase:
@@ -480,7 +480,7 @@ ESCAPED_OPERATORS = [
 
 operator_regexp = "|".join([ re.escape(i) for i in OPERATORS ] + ESCAPED_OPERATORS)
 
-word_regexp = ur'[a-zA-Z_\u00a0-\ufffd][0-9a-zA-Z_\u00a0-\ufffd]*'
+word_regexp = r'[a-zA-Z_\u00a0-\ufffd][0-9a-zA-Z_\u00a0-\ufffd]*'
 
 class Lexer(object):
     """
@@ -564,7 +564,7 @@ class Lexer(object):
 
         # print self.text[self.pos].encode('unicode_escape')
 
-        self.match_regexp(ur"(\s+|\\\n)+")
+        self.match_regexp(r"(\s+|\\\n)+")
 
     def match(self, regexp):
         """
@@ -689,7 +689,7 @@ class Lexer(object):
             s = s.replace("\\[", "[[")
             s = s.replace("\\%", "%%")
             s = re.sub(r'\\u([0-9a-fA-F]{1,4})',
-                       lambda m : unichr(int(m.group(1), 16)), s)
+                       lambda m : chr(int(m.group(1), 16)), s)
             s = re.sub(r'\\(.)', r'\1', s)
 
         return s
@@ -1005,7 +1005,7 @@ class Lexer(object):
             name = name or thing
             rv = self.match(thing)
         else:
-            name = name or thing.im_func.func_name
+            name = name or thing.__func__.__name__
             rv = thing()
 
         if rv is None:
@@ -2315,7 +2315,7 @@ def parse_block(l):
             else:
                 rv.append(stmt)
 
-        except ParseError, e:
+        except ParseError as e:
             parse_errors.append(e.message)
             l.advance()
 
@@ -2339,7 +2339,7 @@ def parse(fn, filedata=None, linenumber=1):
     try:
         lines = list_logical_lines(fn, filedata, linenumber)
         nested = group_logical_lines(lines)
-    except ParseError, e:
+    except ParseError as e:
         parse_errors.append(e.message)
         return None
 
@@ -2369,11 +2369,11 @@ def report_parse_errors():
     full_text = ""
 
     f, error_fn = renpy.error.open_error_file("errors.txt", "w")
-    f.write(codecs.BOM_UTF8)
+    f.write(str(codecs.BOM_UTF8))
 
-    print >>f, "I'm sorry, but errors were detected in your script. Please correct the"
-    print >>f, "errors listed below, and try again."
-    print >>f
+    print("I'm sorry, but errors were detected in your script. Please correct the", file=f)
+    print("errors listed below, and try again.", file=f)
+    print(file=f)
 
     for i in parse_errors:
 
@@ -2385,14 +2385,14 @@ def report_parse_errors():
         except:
             pass
 
-        print
-        print >>f
-        print i
-        print >>f, i
+        print()
+        print(file=f)
+        print(i)
+        print(i, file=f)
 
 
-    print >>f
-    print >>f, "Ren'Py Version:", renpy.version
+    print(file=f)
+    print("Ren'Py Version:", renpy.version, file=f)
 
     f.close()
 
diff --git a/renpy/persistent.py b/renpy/persistent.py
index 2201010..8e81dd2 100644
--- a/renpy/persistent.py
+++ b/renpy/persistent.py
@@ -26,7 +26,7 @@ import time
 import renpy
 
 from renpy.loadsave import dump, loads
-from cPickle import dumps
+from pickle import dumps
 
 # The class that's used to hold the persistent data.
 class Persistent(object):
@@ -178,7 +178,7 @@ def load(filename):
 
     # Unserialize the persistent data.
     try:
-        f = file(filename, "rb")
+        f = open(filename, "rb")
         s = f.read().decode("zlib")
         f.close()
         persistent = loads(s)
@@ -207,7 +207,7 @@ def init():
     # Create the backup of the persistent data.
     v = vars(persistent)
 
-    for k, v in vars(persistent).iteritems():
+    for k, v in vars(persistent).items():
         backup[k] = safe_deepcopy(v)
 
     return persistent
@@ -401,7 +401,7 @@ class _MultiPersistent(object):
     def save(self):
 
         fn = self._filename
-        f = file(fn + ".new", "wb")
+        f = open(fn + ".new", "wb")
         dump(self, f)
         f.close()
 
@@ -451,7 +451,7 @@ def MultiPersistent(name):
             break
 
     try:
-        rv = loads(file(fn).read())
+        rv = loads(open(fn).read())
     except:
         rv = _MultiPersistent()
 
diff --git a/renpy/pyanalysis.py b/renpy/pyanalysis.py
index f5267bb..041ea8d 100644
--- a/renpy/pyanalysis.py
+++ b/renpy/pyanalysis.py
@@ -19,11 +19,6 @@
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-from __future__ import print_function
-from __future__ import unicode_literals
-from __future__ import division
-from __future__ import absolute_import
-
 import renpy # @UnusedImport
 from renpy.python import py_compile
 
@@ -31,7 +26,7 @@ from renpy.python import py_compile
 import ast
 
 import zlib
-from cPickle import loads, dumps
+from pickle import loads, dumps
 
 # The set of names that should be treated as constants.
 always_constants = { 'True', 'False', 'None' }
@@ -45,7 +40,7 @@ pure_functions = {
     "getattr", "globals", "hasattr", "hash", "hex", "int", "isinstance",
     "len", "list", "long", "map", "max", "min", "oct", "ord", "pow",
     "range", "reduce", "repr", "round", "set", "sorted",
-    "str", "sum", "tuple", "unichr", "unicode", "vars", "zip",
+    "str", "sum", "tuple", "vars", "zip",
 
     # enumerator and reversed return iterators at the moment.
 
@@ -137,7 +132,7 @@ def pure(fn):
 
     name = fn
 
-    if not isinstance(name, basestring):
+    if not isinstance(name, str):
         name = fn.__name__
 
     if name not in not_constants:
@@ -297,7 +292,7 @@ class Analysis(object):
         not changed since the last time we called this function.
         """
 
-        for i in self.children.values():
+        for i in list(self.children.values()):
             if not i.at_fixed_point():
                 return False
 
diff --git a/renpy/python.py b/renpy/python.py
index 22e7405..22f5ecb 100644
--- a/renpy/python.py
+++ b/renpy/python.py
@@ -30,7 +30,6 @@ import marshal
 import random
 import weakref
 import re
-import sets
 import sys
 
 import renpy.audio
@@ -121,7 +120,7 @@ class StoreDict(dict):
             if k not in self.old:
                 rv[k] = deleted
 
-        for k, v in self.old.iteritems():
+        for k, v in self.old.items():
 
             new_v = self.get(k, deleted)
 
@@ -168,7 +167,7 @@ def create_store(name):
     # Set up the default contents of the store.
     eval("1", d)
 
-    for k, v in renpy.minstore.__dict__.iteritems():
+    for k, v in renpy.minstore.__dict__.items():
         if k not in d:
             d[k] = v
 
@@ -200,14 +199,14 @@ class StoreBackup():
         self.ever_been_changed = { }
 
 
-        for k, v in store_dicts.iteritems():
+        for k, v in store_dicts.items():
             self.store[k] = dict(v)
             self.old[k] = dict(v.old)
             self.ever_been_changed[k] = set(v.ever_been_changed)
 
     def restore(self):
 
-        for k, sd in store_dicts.iteritems():
+        for k, sd in store_dicts.items():
 
             sd.clear()
             sd.update(self.store[k])
@@ -229,7 +228,7 @@ def make_clean_stores():
 
     global clean_store_backup
 
-    for _k, v in store_dicts.iteritems():
+    for _k, v in store_dicts.items():
 
         v.old.clear()
         v.ever_been_changed.clear()
@@ -294,14 +293,14 @@ def reached(obj, reachable, wait):
 
     try:
         # Treat as fields, indexed by strings.
-        for v in vars(obj).itervalues():
+        for v in vars(obj).values():
             reached(v, reachable, wait)
     except:
         pass
 
     try:
         # Treat as iterable
-        if not isinstance(obj, basestring):
+        if not isinstance(obj, str):
             for v in obj.__iter__():
                 reached(v, reachable, wait)
     except:
@@ -309,7 +308,7 @@ def reached(obj, reachable, wait):
 
     try:
         # Treat as dict.
-        for v in obj.itervalues():
+        for v in obj.values():
             reached(v, reachable, wait)
     except:
         pass
@@ -326,14 +325,14 @@ def reached_vars(store, reachable, wait):
     the path by which the object was reached.
     """
 
-    for v in store.itervalues():
+    for v in store.values():
         reached(v, reachable, wait)
 
     for c in renpy.game.contexts:
         reached(c.info, reachable, wait)
         reached(c.music, reachable, wait)
         for d in c.dynamic_stack:
-            for v in d.itervalues():
+            for v in d.values():
                 reached(v, reachable, wait)
 
 
@@ -417,7 +416,7 @@ def set_filename(filename, offset, tree):
         worklist.extend(node.getChildNodes())
 
 
-unicode_re = re.compile(ur'[\u0080-\uffff]')
+unicode_re = re.compile(r'[\u0080-\uffff]')
 
 def unicode_sub(m):
     """
@@ -483,7 +482,7 @@ def py_compile(source, mode, filename='<none>', lineno=1, ast_node=False):
         filename = source.filename
         lineno = source.linenumber
 
-    source = unicode(source)
+    source = str(source)
     source = source.replace("\r", "")
     source = escape_unicode(source)
 
@@ -504,7 +503,7 @@ def py_compile(source, mode, filename='<none>', lineno=1, ast_node=False):
 
         return compile(tree, filename, mode)
 
-    except SyntaxError, e:
+    except SyntaxError as e:
 
         if e.lineno is not None:
             e.lineno += line_offset
@@ -558,7 +557,6 @@ class RevertableList(list):
         list.__init__(self, *args)
 
     __delitem__ = mutator(list.__delitem__)
-    __delslice__ = mutator(list.__delslice__)
     __setitem__ = mutator(list.__setitem__)
     __iadd__ = mutator(list.__iadd__)
     __imul__ = mutator(list.__imul__)
@@ -577,7 +575,6 @@ class RevertableList(list):
         return newmethod
 
     __add__ = wrapper(list.__add__)
-    __getslice__ = wrapper(list.__getslice__)
     __mul__ = wrapper(list.__mul__)
     __rmul__ = wrapper(list.__rmul__)
 
@@ -590,7 +587,7 @@ class RevertableList(list):
         self[:] = old
 
 def revertable_range(*args):
-    return RevertableList(range(*args))
+    return RevertableList(list(range(*args)))
 
 def revertable_sorted(*args, **kwargs):
     return RevertableList(sorted(*args, **kwargs))
@@ -630,7 +627,7 @@ class RevertableDict(dict):
         return rv
 
     def get_rollback(self):
-        return self.items()
+        return list(self.items())
 
     def rollback(self, old):
         self.clear()
@@ -638,7 +635,7 @@ class RevertableDict(dict):
         for k, v in old:
             self[k] = v
 
-class RevertableSet(sets.Set):
+class RevertableSet(set):
 
     def __init__(self, *args):
         log = renpy.game.log
@@ -646,44 +643,43 @@ class RevertableSet(sets.Set):
         if log is not None:
             log.mutated[id(self)] = None
 
-        sets.Set.__init__(self, *args)
-
-    __iand__ = mutator(sets.Set.__iand__)
-    __ior__ = mutator(sets.Set.__ior__)
-    __isub__ = mutator(sets.Set.__isub__)
-    __ixor__ = mutator(sets.Set.__ixor__)
-    add = mutator(sets.Set.add)
-    clear = mutator(sets.Set.clear)
-    difference_update = mutator(sets.Set.difference_update)
-    discard = mutator(sets.Set.discard)
-    intersection_update = mutator(sets.Set.intersection_update)
-    pop = mutator(sets.Set.pop)
-    remove = mutator(sets.Set.remove)
-    symmetric_difference_update = mutator(sets.Set.symmetric_difference_update)
-    union_update = mutator(sets.Set.union_update)
-    update = mutator(sets.Set.update)
+        set.__init__(self, *args)
+
+    __iand__ = mutator(set.__iand__)
+    __ior__ = mutator(set.__ior__)
+    __isub__ = mutator(set.__isub__)
+    __ixor__ = mutator(set.__ixor__)
+    add = mutator(set.add)
+    clear = mutator(set.clear)
+    difference_update = mutator(set.difference_update)
+    discard = mutator(set.discard)
+    intersection_update = mutator(set.intersection_update)
+    pop = mutator(set.pop)
+    remove = mutator(set.remove)
+    symmetric_difference_update = mutator(set.symmetric_difference_update)
+    union = mutator(set.union)
+    update = mutator(set.update)
 
     def wrapper(method): # E0213 @NoSelf
         def newmethod(*args, **kwargs):
             rv = method(*args, **kwargs) # E1102
-            if isinstance(rv, sets.Set):
+            if isinstance(rv, set):
                 return RevertableSet(rv)
             else:
                 return rv
 
         return newmethod
 
-    __and__ = wrapper(sets.Set.__and__)
-    __copy__ = wrapper(sets.Set.__copy__)
-    __deepcopy__ = wrapper(sets.Set.__deepcopy__)
-    __sub__ = wrapper(sets.Set.__sub__)
-    __xor__ = wrapper(sets.Set.__xor__)
-    __or__ = wrapper(sets.Set.__or__)
-    copy = wrapper(sets.Set.copy)
-    difference = wrapper(sets.Set.difference)
-    intersection = wrapper(sets.Set.intersection)
-    symmetric_difference = wrapper(sets.Set.symmetric_difference)
-    union = wrapper(sets.Set.union)
+    __and__ = wrapper(set.__and__)
+    copy = wrapper(set.copy)
+    __sub__ = wrapper(set.__sub__)
+    __xor__ = wrapper(set.__xor__)
+    __or__ = wrapper(set.__or__)
+    copy = wrapper(set.copy)
+    difference = wrapper(set.difference)
+    intersection = wrapper(set.intersection)
+    symmetric_difference = wrapper(set.symmetric_difference)
+    union = wrapper(set.union)
 
     del wrapper
 
@@ -691,8 +687,8 @@ class RevertableSet(sets.Set):
         return list(self)
 
     def rollback(self, old):
-        sets.Set.clear(self)
-        sets.Set.update(self, old)
+        set.clear(self)
+        set.update(self, old)
 
 
 class RevertableObject(object):
@@ -869,15 +865,15 @@ class Rollback(renpy.object.Object):
 
         # Add objects reachable from the stores. (Objects that might be
         # unreachable at the moment.)
-        for changes in self.stores.itervalues():
-            for _k, v in changes.iteritems():
+        for changes in self.stores.values():
+            for _k, v in changes.items():
                 if v is not deleted:
                     reached(v, reachable, wait)
 
         # Add in objects reachable through the context.
         reached(self.context.info, reachable, wait)
         for d in self.context.dynamic_stack:
-            for v in d.itervalues():
+            for v in d.values():
                 reached(v, reachable, wait)
 
         # Add in objects reachable through displayables.
@@ -892,7 +888,7 @@ class Rollback(renpy.object.Object):
                 reached(rb, reachable, wait)
             else:
                 if renpy.config.debug:
-                    print "Removing unreachable:", o
+                    print("Removing unreachable:", o)
 
                     pass
 
@@ -911,12 +907,12 @@ class Rollback(renpy.object.Object):
             if roll is not None:
                 obj.rollback(roll)
 
-        for name, changes in self.stores.iteritems():
+        for name, changes in self.stores.items():
             store = store_dicts.get(name, None)
             if store is None:
                 return
 
-            for name, value in changes.iteritems():
+            for name, value in changes.items():
                 if value is deleted:
                     if name in store:
                         del store[name]
@@ -1045,7 +1041,7 @@ class RollbackLog(renpy.object.Object):
         self.rolled_forward = False
 
         # Reset the point that changes are relative to.
-        for sd in store_dicts.itervalues():
+        for sd in store_dicts.values():
             sd.begin()
 
     def complete(self):
@@ -1059,18 +1055,18 @@ class RollbackLog(renpy.object.Object):
 
         # Update self.current.stores with the changes from each store.
         # Also updates .ever_been_changed.
-        for name, sd in store_dicts.iteritems():
+        for name, sd in store_dicts.items():
             self.current.stores[name] = sd.get_changes()
 
         # Update the list of mutated objects and what we need to do to
         # restore them.
 
-        for _i in xrange(4):
+        for _i in range(4):
 
             self.current.objects = [ ]
 
             try:
-                for _k, v in self.mutated.iteritems():
+                for _k, v in self.mutated.items():
 
                     if v is None:
                         continue
@@ -1102,7 +1098,7 @@ class RollbackLog(renpy.object.Object):
 
         rv = { }
 
-        for store_name, sd in store_dicts.iteritems():
+        for store_name, sd in store_dicts.items():
             for name in sd.ever_been_changed:
                 if name in sd:
                     rv[store_name + "." + name] = sd[name]
@@ -1300,7 +1296,7 @@ class RollbackLog(renpy.object.Object):
 
             # Otherwise, just give up.
 
-            print "Can't find a place to rollback to. Not rolling back."
+            print("Can't find a place to rollback to. Not rolling back.")
 
             revlog.reverse()
             self.log = self.log + revlog
@@ -1422,7 +1418,7 @@ class RollbackLog(renpy.object.Object):
         clean_stores()
         renpy.translation.init_translation()
 
-        for name, value in roots.iteritems():
+        for name, value in roots.items():
 
             if "." in name:
                 store_name, name = name.rsplit(".", 1)
@@ -1458,7 +1454,7 @@ def py_exec_bytecode(bytecode, hide=False, globals=None, locals=None, store="sto
     if locals is None:
         locals = globals #@ReservedAssignment
 
-    exec bytecode in globals, locals
+    exec(bytecode, globals, locals)
 
 
 def py_exec(source, hide=False, store=None):
@@ -1471,7 +1467,7 @@ def py_exec(source, hide=False, store=None):
     else:
         locals = store #@ReservedAssignment
 
-    exec py_compile(source, 'exec') in store, locals
+    exec(py_compile(source, 'exec'), store, locals)
 
 
 def py_eval_bytecode(bytecode, globals=None, locals=None): #@ReservedAssignment
@@ -1485,7 +1481,7 @@ def py_eval_bytecode(bytecode, globals=None, locals=None): #@ReservedAssignment
     return eval(bytecode, globals, locals)
 
 def py_eval(code, globals=None, locals=None): #@ReservedAssignment
-    if isinstance(code, basestring):
+    if isinstance(code, str):
         code = py_compile(code, 'eval')
     return py_eval_bytecode(code, globals, locals)
 
@@ -1505,7 +1501,7 @@ def raise_at_location(e, loc):
     code = compile(node, filename, 'exec')
 
     # PY3 - need to change to exec().
-    exec code in { "e" : e }
+    exec(code, { "e" : e })
 
 
 # This was used to proxy accesses to the store. Now it's kept around to deal
@@ -1524,18 +1520,18 @@ class StoreProxy(object):
 
 # Code for pickling bound methods.
 def method_pickle(method):
-    name = method.im_func.__name__
+    name = method.__func__.__name__
 
-    obj = method.im_self
+    obj = method.__self__
 
     if obj is None:
-        obj = method.im_class
+        obj = method.__self__.__class__
 
     return method_unpickle, (obj, name)
 
 def method_unpickle(obj, name):
     return getattr(obj, name)
 
-import copy_reg
+import copyreg
 import types
-copy_reg.pickle(types.MethodType, method_pickle, method_unpickle)
+copyreg.pickle(types.MethodType, method_pickle, method_unpickle)
diff --git a/renpy/savelocation.py b/renpy/savelocation.py
index 9fc813a..567ccc5 100644
--- a/renpy/savelocation.py
+++ b/renpy/savelocation.py
@@ -113,7 +113,7 @@ class FileLocation(object):
 
             self.mtimes = new_mtimes
 
-            for slotname, mtime in new_mtimes.iteritems():
+            for slotname, mtime in new_mtimes.items():
                 if old_mtimes.get(slotname, None) != mtime:
                     clear_slot(slotname)
 
diff --git a/renpy/screenlang.py b/renpy/screenlang.py
index 4a537eb..b393a53 100644
--- a/renpy/screenlang.py
+++ b/renpy/screenlang.py
@@ -231,12 +231,12 @@ class Parser(object):
         and expr instances, and adjusts the line number.
         """
 
-        if isinstance(expr, unicode):
+        if isinstance(expr, str):
             expr = renpy.python.escape_unicode(expr)
 
         try:
             rv = ast.parse(expr, 'eval').body[0].value
-        except SyntaxError, e:
+        except SyntaxError as e:
             raise renpy.parser.ParseError(
                 filename,
                 lineno + e[1][1] - 1,
@@ -254,12 +254,12 @@ class Parser(object):
         adjusts the line number. Returns a list of statements.
         """
 
-        if isinstance(code, unicode):
+        if isinstance(code, str):
             code = renpy.python.escape_unicode(code)
 
         try:
             rv = ast.parse(code, 'exec')
-        except SyntaxError, e:
+        except SyntaxError as e:
 
             raise renpy.parser.ParseError(
                 filename,
diff --git a/renpy/script.py b/renpy/script.py
index 61d6b5f..5f31183 100644
--- a/renpy/script.py
+++ b/renpy/script.py
@@ -27,13 +27,13 @@ import renpy
 import os
 import imp
 import difflib
-import md5
+import hashlib
 import time
 import marshal
 import struct
 import zlib
 
-from cPickle import loads, dumps
+from pickle import loads, dumps
 import shutil
 
 # The version of the dumped script.
@@ -103,7 +103,7 @@ class Script(object):
         renpy.game.script = self
 
         if os.path.exists(renpy.config.renpy_base + "/lock.txt"):
-            self.key = file(renpy.config.renpy_base + "/lock.txt", "rb").read()
+            self.key = open(renpy.config.renpy_base + "/lock.txt", "rb").read()
         else:
             self.key = None
 
@@ -130,7 +130,8 @@ class Script(object):
 
         self.serial = 0
 
-        self.digest = md5.md5(renpy.version_only)
+        self.digest = hashlib.md5(renpy.version_only.encode())
+        self.md5 = hashlib.md5
 
         self.loaded_rpy = False
         self.backup_list = [ ]
@@ -195,7 +196,7 @@ class Script(object):
                 continue
 
             try:
-                os.makedirs(os.path.dirname(target_fn), 0700)
+                os.makedirs(os.path.dirname(target_fn), 0o700)
             except:
                 pass
 
@@ -402,7 +403,7 @@ class Script(object):
                 name = node.name
 
                 if name in self.namemap:
-                    if not isinstance(bad_name, basestring):
+                    if not isinstance(bad_name, str):
                         bad_name = name
                         bad_node = node
                         old_node = self.namemap[name]
@@ -566,7 +567,7 @@ class Script(object):
             self.assign_names(stmts, fullfn)
 
             try:
-                f = file(rpycfn, "wb")
+                f = open(rpycfn, "wb")
 
                 self.write_rpyc_header(f)
                 self.write_rpyc_data(f, 1, dumps((data, stmts), 2))
@@ -579,7 +580,7 @@ class Script(object):
                 self.write_rpyc_data(f, 2, dumps((data, stmts), 2))
 
                 with open(fullfn, "rU") as fullf:
-                    rpydigest = md5.md5(fullf.read()).digest()
+                    rpydigest = hashlib.md5(fullf.read().encode()).digest()
 
                 self.write_rpyc_md5(f, rpydigest)
 
@@ -615,7 +616,7 @@ class Script(object):
                     return None, None
 
                 if data is None:
-                    print "Failed to load", fn
+                    print("Failed to load", fn)
                     return None, None
 
                 if not isinstance(data, dict):
@@ -652,8 +653,8 @@ class Script(object):
                 raise Exception("Could not load from archive %s." % (lastfn,))
 
             f = renpy.loader.load(fn + compiled)
-            f.seek(-md5.digest_size, 2)
-            digest = f.read(md5.digest_size)
+            f.seek(-self.md5.digest_size, 2)
+            digest = f.read(self.md5.digest_size)
             f.close()
 
         else:
@@ -667,15 +668,15 @@ class Script(object):
 
             if os.path.exists(rpyfn):
                 with open(rpyfn, "rU") as f:
-                    rpydigest = md5.md5(f.read()).digest()
+                    rpydigest = hashlib.md5(f.read().encode()).digest()
             else:
                 rpydigest = None
 
             try:
                 if os.path.exists(rpycfn):
                     with open(rpycfn, "rb") as f:
-                        f.seek(-md5.digest_size, 2)
-                        rpycdigest = f.read(md5.digest_size)
+                        f.seek(-self.md5.digest_size, 2)
+                        rpycdigest = f.read(self.md5.digest_size)
                 else:
                     rpycdigest = None
             except:
@@ -698,11 +699,11 @@ class Script(object):
                         data, stmts = self.load_file(dir, fn + compiled)
 
                         if data is None:
-                            print "Could not load " + rpycfn
+                            print("Could not load " + rpycfn)
 
                 except:
                     if "RENPY_RPYC_EXCEPTIONS" in os.environ:
-                        print "While loading", rpycfn
+                        print("While loading", rpycfn)
                         raise
 
                     pass
@@ -763,7 +764,7 @@ class Script(object):
         # bytecode.
         for i in self.all_pycode:
 
-            key = i.get_hash() + MAGIC
+            key = i.get_hash() + str(MAGIC)
 
             code = self.bytecode_oldcache.get(key, None)
 
@@ -781,7 +782,7 @@ class Script(object):
                     elif i.mode == 'eval':
                         code = renpy.python.py_compile_eval_bytecode(i.source, filename=i.location[0], lineno=i.location[1])
 
-                except SyntaxError, e:
+                except SyntaxError as e:
 
                     text = e.text
 
@@ -789,9 +790,9 @@ class Script(object):
                         text = ''
 
                     try:
-                        text = text.decode("utf-8")
+                        text = text.encode().decode("utf-8")
                     except:
-                        text = text.decode("latin-1")
+                        text = text.encode().decode("latin-1")
 
                     pem = renpy.parser.ParseError(
                         filename = e.filename,
diff --git a/renpy/scriptedit.py b/renpy/scriptedit.py
index 92994d0..6818c59 100644
--- a/renpy/scriptedit.py
+++ b/renpy/scriptedit.py
@@ -90,7 +90,7 @@ def adjust_line_locations(filename, linenumber, char_offset, line_offset):
 
     new_lines = { }
 
-    for key, line in lines.iteritems():
+    for key, line in lines.items():
 
         (fn, ln) = key
 
@@ -204,13 +204,13 @@ def first_and_last_nodes(nodes):
 
     for i in nodes:
         for j in nodes:
-            if j.next is i:
+            if j.__next__ is i:
                 break
         else:
             firsts.append(i)
 
         for j in nodes:
-            if i.next is j:
+            if i.__next__ is j:
                 break
 
         else:
@@ -288,7 +288,7 @@ def remove_from_ast(filename, linenumber):
         if i in nodes:
             continue
 
-        i.replace_next(first, last.next)
+        i.replace_next(first, last.__next__)
 
         new_stmts.append(i)
 
diff --git a/renpy/sl2/slast.py b/renpy/sl2/slast.py
index defd86f..78c2f01 100644
--- a/renpy/sl2/slast.py
+++ b/renpy/sl2/slast.py
@@ -30,7 +30,7 @@
 import ast
 import collections
 import linecache
-from cPickle import loads, dumps
+from pickle import loads, dumps
 import zlib
 
 import renpy.display
@@ -43,7 +43,8 @@ from renpy.display.predict import displayable as predict_displayable
 
 from renpy.python import py_eval_bytecode
 from renpy.pyanalysis import Analysis, NOT_CONST, GLOBAL_CONST, ccache
-import md5
+import hashlib
+md5 = hashlib.md5
 
 # This file contains the abstract syntax tree for a screen language
 # screen.
@@ -1398,7 +1399,7 @@ class SLFor(SLBlock):
         if c is None:
             return
 
-        for child_cache in c.values():
+        for child_cache in list(c.values()):
             for i in self.children:
                 i.copy_on_change(child_cache)
 
@@ -1422,7 +1423,7 @@ class SLPython(SLNode):
         analysis.python(self.code.source)
 
     def execute(self, context):
-        exec self.code.bytecode in context.globals, context.scope
+        exec(self.code.bytecode, context.globals, context.scope)
 
     def prepare(self, analysis):
         self.constant = NOT_CONST
diff --git a/renpy/styledata/styleutil.py b/renpy/styledata/styleutil.py
index fcd65c1..054f7c8 100644
--- a/renpy/styledata/styleutil.py
+++ b/renpy/styledata/styleutil.py
@@ -22,6 +22,7 @@
 # Utility functions used by the various property functions:
 
 import renpy
+import collections
 
 def none_is_null(o):
     if o is None:
@@ -36,7 +37,7 @@ def expand_focus_mask(v):
         return v
     elif v is True:
         return v
-    elif callable(v):
+    elif isinstance(v, collections.Callable):
         return v
     else:
         return renpy.easy.displayable(v)
diff --git a/renpy/text/extras.py b/renpy/text/extras.py
index 2fa4d49..75e2f3f 100644
--- a/renpy/text/extras.py
+++ b/renpy/text/extras.py
@@ -73,7 +73,7 @@ def check_text_tags(s):
     else:
         all_tags = text_tags
 
-    tokens = textsupport.tokenize(unicode(s))
+    tokens = textsupport.tokenize(str(s))
 
     tag_stack = [ ]
 
diff --git a/renpy/text/font.py b/renpy/text/font.py
index 07ac3ab..12e4d0f 100644
--- a/renpy/text/font.py
+++ b/renpy/text/font.py
@@ -92,7 +92,7 @@ class ImageFont(object):
             return
 
         for g in glyphs:
-            c = unichr(g.character)
+            c = chr(g.character)
 
             cxo, cyo = self.offsets[c]
             x = g.x + xo + cxo
@@ -143,20 +143,20 @@ class SFont(ImageFont):
         self.baseline = height # W0201
 
         # Create space characters.
-        self.chars[u' '] = renpy.display.pgrender.surface((self.spacewidth, height), True)
-        self.width[u' '] = self.spacewidth
-        self.advance[u' '] = self.spacewidth
-        self.offsets[u' '] = (0, 0)
+        self.chars[' '] = renpy.display.pgrender.surface((self.spacewidth, height), True)
+        self.width[' '] = self.spacewidth
+        self.advance[' '] = self.spacewidth
+        self.offsets[' '] = (0, 0)
 
-        self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, height), True)
-        self.width[u'\u200b'] = 0
-        self.advance[u'\u200b'] = 0
-        self.offsets[u'\u200b'] = (0, 0)
+        self.chars['\u200b'] = renpy.display.pgrender.surface((0, height), True)
+        self.width['\u200b'] = 0
+        self.advance['\u200b'] = 0
+        self.offsets['\u200b'] = (0, 0)
 
-        self.chars[u'\u00a0'] = self.chars[u' ']
-        self.width[u'\u00a0'] = self.width[u' ']
-        self.advance[u'\u00a0'] = self.advance[u' ']
-        self.offsets[u'\u00a0'] = self.offsets[u' ']
+        self.chars['\u00a0'] = self.chars[' ']
+        self.width['\u00a0'] = self.width[' ']
+        self.advance['\u00a0'] = self.advance[' ']
+        self.offsets['\u00a0'] = self.offsets[' ']
 
         # The color key used to separate characters.
         i = 0
@@ -234,7 +234,7 @@ class MudgeFont(ImageFont):
             if char < 0:
                 continue
 
-            c = unichr(char)
+            c = chr(char)
             x = int(e.attrib["x"])
             y = int(e.attrib["y"])
             w = int(e.attrib["width"])
@@ -254,22 +254,22 @@ class MudgeFont(ImageFont):
         self.baseline = height # W0201
 
         # Create space characters.
-        if u' ' not in self.chars:
-            self.chars[u' '] = renpy.display.pgrender.surface((self.spacewidth, height), True)
-            self.width[u' '] = self.spacewidth
-            self.advance[u' '] = self.spacewidth
-            self.offsets[u' '] = (0, 0)
+        if ' ' not in self.chars:
+            self.chars[' '] = renpy.display.pgrender.surface((self.spacewidth, height), True)
+            self.width[' '] = self.spacewidth
+            self.advance[' '] = self.spacewidth
+            self.offsets[' '] = (0, 0)
 
-        if u'\u00a0' not in self.chars:
-            self.chars[u'\u00a0'] = self.chars[u' ']
-            self.width[u'\u00a0'] = self.width[u' ']
-            self.advance[u'\u00a0'] = self.advance[u' ']
-            self.offsets[u'\u00a0'] = self.offsets[u' ']
+        if '\u00a0' not in self.chars:
+            self.chars['\u00a0'] = self.chars[' ']
+            self.width['\u00a0'] = self.width[' ']
+            self.advance['\u00a0'] = self.advance[' ']
+            self.offsets['\u00a0'] = self.offsets[' ']
 
-        self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, height), True)
-        self.width[u'\u200b'] = 0
-        self.advance[u'\u200b'] = 0
-        self.offsets[u'\u200b'] = (0, 0)
+        self.chars['\u200b'] = renpy.display.pgrender.surface((0, height), True)
+        self.width['\u200b'] = 0
+        self.advance['\u200b'] = 0
+        self.offsets['\u200b'] = (0, 0)
 
 
 
@@ -330,7 +330,7 @@ class BMFont(ImageFont):
             elif kind == "page":
                 pages[int(args["id"])] = renpy.display.im.Image(args["file"]).load(unscaled=True)
             elif kind == "char":
-                c = unichr(int(args["id"]))
+                c = chr(int(args["id"]))
                 x = int(args["x"])
                 y = int(args["y"])
                 w = int(args["width"])
@@ -350,17 +350,17 @@ class BMFont(ImageFont):
 
         f.close()
 
-        if u'\u00a0' not in self.chars:
-            self.chars[u'\u00a0'] = self.chars[u' ']
-            self.width[u'\u00a0'] = self.width[u' ']
-            self.advance[u'\u00a0'] = self.advance[u' ']
-            self.offsets[u'\u00a0'] = self.offsets[u' ']
+        if '\u00a0' not in self.chars:
+            self.chars['\u00a0'] = self.chars[' ']
+            self.width['\u00a0'] = self.width[' ']
+            self.advance['\u00a0'] = self.advance[' ']
+            self.offsets['\u00a0'] = self.offsets[' ']
 
 
-        self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, self.height), True)
-        self.width[u'\u200b'] = 0
-        self.advance[u'\u200b'] = 0
-        self.offsets[u'\u200b'] = (0, 0)
+        self.chars['\u200b'] = renpy.display.pgrender.surface((0, self.height), True)
+        self.width['\u200b'] = 0
+        self.advance['\u200b'] = 0
+        self.offsets['\u200b'] = (0, 0)
 
 class ScaledImageFont(ImageFont):
     """
@@ -376,14 +376,14 @@ class ScaledImageFont(ImageFont):
         self.baseline = scale(parent.baseline)
         self.default_kern = scale(parent.default_kern)
 
-        self.width = { k : scale(v) for k, v in parent.width.iteritems() }
-        self.advance = { k : scale(v) for k, v in parent.advance.iteritems() }
-        self.offsets = { k : (scale(v[0]), scale(v[1])) for k, v in parent.offsets.iteritems() }
-        self.kerns = { k : scale(v) for k, v in parent.kerns.iteritems() }
+        self.width = { k : scale(v) for k, v in parent.width.items() }
+        self.advance = { k : scale(v) for k, v in parent.advance.items() }
+        self.offsets = { k : (scale(v[0]), scale(v[1])) for k, v in parent.offsets.items() }
+        self.kerns = { k : scale(v) for k, v in parent.kerns.items() }
 
         self.chars = { }
 
-        for k, v in parent.chars.iteritems():
+        for k, v in parent.chars.items():
             w, h = v.get_size()
             nw = scale(w)
             nh = scale(h)
@@ -392,7 +392,7 @@ class ScaledImageFont(ImageFont):
 
 def register_sfont(name=None, size=None, bold=False, italics=False, underline=False,
                    filename=None, spacewidth=10, default_kern=0, kerns={},
-                   charset=u"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"):
+                   charset="!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"):
 
     """
     :doc: image_fonts
@@ -570,12 +570,12 @@ def load_face(fn):
 
         pygame.sysfont.initsysfonts()
 
-        for v in pygame.sysfont.Sysfonts.itervalues():
+        for v in pygame.sysfont.Sysfonts.values():
             if v is not None:
-                for _flags, ffn in v.iteritems():
+                for _flags, ffn in v.items():
                     for i in fonts:
                         if ffn.lower().endswith(i):
-                            font_file = file(ffn, "rb")
+                            font_file = open(ffn, "rb")
                             break
 
                     if font_file:
@@ -661,7 +661,7 @@ def free_memory():
 
 
 def load_image_fonts():
-    for i in image_fonts.itervalues():
+    for i in image_fonts.values():
         i.load()
 
 
diff --git a/renpy/text/text.py b/renpy/text/text.py
index 0d78ddf..748dbbd 100644
--- a/renpy/text/text.py
+++ b/renpy/text/text.py
@@ -844,7 +844,7 @@ class Layout(object):
                 if isinstance(i[0], (TextSegment, SpaceSegment, DisplayableSegment)):
                     return
 
-            line.extend(tss[-1].subsegment(u" "))
+            line.extend(tss[-1].subsegment(" "))
 
         for type, text in tokens: #@ReservedAssignment
 
@@ -864,7 +864,7 @@ class Layout(object):
                 continue
 
             elif type == DISPLAYABLE:
-                line.append((DisplayableSegment(tss[-1], text, renders), u""))
+                line.append((DisplayableSegment(tss[-1], text, renders), ""))
                 continue
 
             # Otherwise, we have a text tag.
@@ -896,7 +896,7 @@ class Layout(object):
 
             elif tag == "space":
                 width = self.scale_int(int(value))
-                line.append((SpaceSegment(tss[-1], width=width), u""))
+                line.append((SpaceSegment(tss[-1], width=width), ""))
 
             elif tag == "vspace":
                 # Duplicates from the newline tag.
@@ -906,7 +906,7 @@ class Layout(object):
                 if line:
                     paragraphs.append(line)
 
-                line = [ (SpaceSegment(tss[-1], height=height), u"") ]
+                line = [ (SpaceSegment(tss[-1], height=height), "") ]
                 paragraphs.append(line)
 
                 line = [ ]
@@ -1295,7 +1295,7 @@ class Text(renpy.display.core.Displayable):
 
         # Check that the text is all text-able things.
         for i in text:
-            if not isinstance(i, (basestring, renpy.display.core.Displayable)):
+            if not isinstance(i, (str, renpy.display.core.Displayable)):
                 if renpy.config.developer:
                     raise Exception("Cannot display {0!r} as text.".format(i))
                 else:
@@ -1345,15 +1345,15 @@ class Text(renpy.display.core.Displayable):
         s = ""
 
         for i in self.text:
-            if isinstance(i, basestring):
+            if isinstance(i, str):
                 s += i
 
             if len(s) > 25:
-                s = s[:24] + u"\u2026"
+                s = s[:24] + "\u2026"
                 break
 
         s = s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
-        return u"Text \"{}\"".format(s)
+        return "Text \"{}\"".format(s)
 
     def _scope(self, scope, update=True):
         """
@@ -1377,12 +1377,12 @@ class Text(renpy.display.core.Displayable):
 
         # Perform substitution as necessary.
         for i in text:
-            if isinstance(i, basestring):
+            if isinstance(i, str):
                 if substitute is not False:
                     i, did_sub = renpy.substitutions.substitute(i, scope, substitute)
                     uses_scope = uses_scope or did_sub
 
-                i = unicode(i)
+                i = str(i)
 
             new_text.append(i)
 
@@ -1482,7 +1482,7 @@ class Text(renpy.display.core.Displayable):
 
         for i in self.text:
 
-            if not isinstance(i, basestring):
+            if not isinstance(i, str):
                 continue
 
             rv.append(i)
@@ -1826,11 +1826,11 @@ class Text(renpy.display.core.Displayable):
 
         for i in text:
 
-            if isinstance(i, unicode):
+            if isinstance(i, str):
                 tokens.extend(textsupport.tokenize(i))
 
             elif isinstance(i, str):
-                tokens.extend(textsupport.tokenize(unicode(i)))
+                tokens.extend(textsupport.tokenize(str(i)))
 
             elif isinstance(i, renpy.display.core.Displayable):
                 tokens.append((DISPLAYABLE, i))
@@ -1854,7 +1854,7 @@ class Text(renpy.display.core.Displayable):
             kind, text = t
 
             if kind == TEXT and renpy.config.replace_text:
-                rv.append((TEXT, unicode(renpy.config.replace_text(text))))
+                rv.append((TEXT, str(renpy.config.replace_text(text))))
 
             elif kind != TAG:
                 rv.append(t)
@@ -1906,7 +1906,7 @@ class Text(renpy.display.core.Displayable):
 
                 for kind2, text2 in new_contents:
                     if isinstance(text2, str):
-                        text2 = unicode(text2)
+                        text2 = str(text2)
 
                     new_tokens.append((kind2, text2))
 
diff --git a/renpy/text/textsupport.pyx b/renpy/text/textsupport.pyx
index feabbc3..5b31613 100644
--- a/renpy/text/textsupport.pyx
+++ b/renpy/text/textsupport.pyx
@@ -392,9 +392,9 @@ def linebreak_debug(list glyphs):
         if g.split == SPLIT_INSTEAD:
             rv += "|"
         elif g.split == SPLIT_BEFORE:
-            rv += "[" + unichr(g.character)
+            rv += "[" + chr(g.character)
         else:
-            rv += unichr(g.character)
+            rv += chr(g.character)
 
     return rv
 
diff --git a/renpy/translation.py b/renpy/translation.py
index 3b62fee..0cd1150 100644
--- a/renpy/translation.py
+++ b/renpy/translation.py
@@ -101,7 +101,7 @@ class ScriptTranslator(object):
                 continue
 
             if n.name.__class__ is not tuple:
-                if isinstance(n.name, basestring):
+                if isinstance(n.name, str):
                     label = n.name
 
             type_n = n.__class__
@@ -360,16 +360,16 @@ class StringTranslator(object):
 
         f = open_tl_file(fn)
 
-        f.write(u"translate {} strings:\n".format(language))
-        f.write(u"\n")
+        f.write("translate {} strings:\n".format(language))
+        f.write("\n")
 
         for i in self.unknown:
 
             i = quote_unicode(i)
 
-            f.write(u"    old \"{}\"\n".format(i))
-            f.write(u"    new \"{}\"\n".format(i))
-            f.write(u"\n")
+            f.write("    old \"{}\"\n".format(i))
+            f.write("    new \"{}\"\n".format(i))
+            f.write("\n")
 
         f.close()
 
@@ -597,13 +597,13 @@ def open_tl_file(fn):
             pass
 
         f = io.open(fn, "a", encoding="utf-8")
-        f.write(u"\ufeff")
+        f.write("\ufeff")
 
     else:
         f = io.open(fn, "a", encoding="utf-8")
 
-    f.write(u"# TODO: Translation updated at {}\n".format(time.strftime("%Y-%m-%d %H:%M")))
-    f.write(u"\n")
+    f.write("# TODO: Translation updated at {}\n".format(time.strftime("%Y-%m-%d %H:%M")))
+    f.write("\n")
 
     return f
 
@@ -682,17 +682,17 @@ class TranslateFile(object):
             if label is None:
                 label = ""
 
-            self.f.write(u"# {}:{}\n".format(t.filename, t.linenumber))
-            self.f.write(u"translate {} {}:\n".format(self.language, t.identifier))
-            self.f.write(u"\n")
+            self.f.write("# {}:{}\n".format(t.filename, t.linenumber))
+            self.f.write("translate {} {}:\n".format(self.language, t.identifier))
+            self.f.write("\n")
 
             for n in t.block:
-                self.f.write(u"    # " + n.get_code() + "\n")
+                self.f.write("    # " + n.get_code() + "\n")
 
             for n in t.block:
-                self.f.write(u"    " + n.get_code(self.filter) + "\n")
+                self.f.write("    " + n.get_code(self.filter) + "\n")
 
-            self.f.write(u"\n")
+            self.f.write("\n")
 
     def write_strings(self):
         """
@@ -715,15 +715,15 @@ class TranslateFile(object):
                 started = True
 
                 self.open()
-                self.f.write(u"translate {} strings:\n".format(self.language))
-                self.f.write(u"\n")
+                self.f.write("translate {} strings:\n".format(self.language))
+                self.f.write("\n")
 
             fs = self.filter(s)
 
-            self.f.write(u"    # {}:{}\n".format(filename, line))
-            self.f.write(u"    old \"{}\"\n".format(quote_unicode(s)))
-            self.f.write(u"    new \"{}\"\n".format(quote_unicode(fs)))
-            self.f.write(u"\n")
+            self.f.write("    # {}:{}\n".format(filename, line))
+            self.f.write("    old \"{}\"\n".format(quote_unicode(s)))
+            self.f.write("    new \"{}\"\n".format(quote_unicode(fs)))
+            self.f.write("\n")
 
 def null_filter(s):
     return s
diff --git a/renpy/ui.py b/renpy/ui.py
index 464440b..26ee95d 100644
--- a/renpy/ui.py
+++ b/renpy/ui.py
@@ -477,7 +477,7 @@ class Wrapper(renpy.object.Object):
 
         try:
             w = self.function(*args, **keyword)
-        except TypeError, e:
+        except TypeError as e:
             etype, e, tb = sys.exc_info(); etype
 
             if tb.tb_next is None:
@@ -799,9 +799,9 @@ def menu(menuitems,
                     text = choice_chosen_style
                     button = choice_chosen_button_style
 
-            if isinstance(button, basestring):
+            if isinstance(button, str):
                 button = getattr(renpy.game.style, button)
-            if isinstance(text, basestring):
+            if isinstance(text, str):
                 text = getattr(renpy.game.style, text)
 
             button = button[label]
@@ -826,7 +826,7 @@ def imagemap_compat(ground,
                     button_style='hotspot',
                     **properties):
 
-    if isinstance(button_style, basestring):
+    if isinstance(button_style, str):
         button_style = getattr(renpy.game.style, button_style)
 
     fixed(style=style, **properties)
@@ -995,7 +995,7 @@ def _bar(*args, **properties):
             else:
                 style = value.get_style()[0]
 
-            if isinstance(style, basestring):
+            if isinstance(style, str):
                 style = style_group_style(style, NoStyleGroupGiven)
 
             properties["style"] = style
@@ -1038,7 +1038,7 @@ def viewport(scrollbars=None, **properties):
     viewport_properties = { }
     side_properties = { }
 
-    for k, v in properties.iteritems():
+    for k, v in properties.items():
         if k.startswith("side_"):
             side_properties[k[5:]] = v
         else:
@@ -1293,7 +1293,7 @@ returns = renpy.curry.curry(_returns)
 
 def _jumps(label, transition=None):
 
-    if isinstance(transition, basestring):
+    if isinstance(transition, str):
         transition = getattr(renpy.config, transition)
 
     if transition is not None:
@@ -1343,6 +1343,6 @@ def screen_id(id_, d):
 
 # Update the wrappers to have names.
 k, v = None, None
-for k, v in globals().iteritems():
+for k, v in globals().items():
     if isinstance(v, Wrapper):
         v.name = k
diff --git a/renpy/warp.py b/renpy/warp.py
index 41bd1be..8675c4e 100644
--- a/renpy/warp.py
+++ b/renpy/warp.py
@@ -24,7 +24,6 @@
 # location.
 
 import renpy
-import sets
 
 warp_spec = None
 
@@ -56,7 +55,7 @@ def warp():
 
     prev = { }
 
-    workset = sets.Set([ n for n in renpy.game.script.namemap.itervalues() if isinstance(n, renpy.ast.Scene) ])
+    workset = sets.Set([ n for n in renpy.game.script.namemap.values() if isinstance(n, renpy.ast.Scene) ])
     seenset = sets.Set(workset)
 
     # This is called to indicate that next can be executed following node.
@@ -100,7 +99,7 @@ def warp():
             add(n, n.get_next())
 
         elif getattr(n, 'next', None) is not None:
-            add(n, n.next)
+            add(n, n.__next__)
 
     # Now, attempt to find a statement preceding the line that the
     # user wants to warp to.
diff --git a/renpy.py b/renpy.py
index 7548cf6..847b8d0 100644
--- a/renpy.py
+++ b/renpy.py
@@ -68,7 +68,7 @@ def path_to_saves(gamedir, save_directory=None):
             if os.path.isdir(rv) and test_writable(rv):
                 break
 
-        print "Saving to", rv
+        print("Saving to", rv)
 
         # We return the last path as the default.
 
@@ -94,7 +94,7 @@ def path_to_saves(gamedir, save_directory=None):
         except:
             rv = url.path.UTF8String().decode("utf-8")
 
-        print "Saving to", rv
+        print("Saving to", rv)
         return rv
 
     # No save directory given.
@@ -151,7 +151,7 @@ try:
     import ast; ast
 except:
     raise
-    print "Ren'Py requires at least python 2.6."
+    print("Ren'Py requires at least python 2.6.")
     sys.exit(0)
 
 android = ("ANDROID_PRIVATE" in os.environ)
@@ -186,8 +186,8 @@ def main():
     try:
         import renpy.bootstrap
     except ImportError:
-        print >>sys.stderr, "Could not import renpy.bootstrap. Please ensure you decompressed Ren'Py"
-        print >>sys.stderr, "correctly, preserving the directory structure."
+        print("Could not import renpy.bootstrap. Please ensure you decompressed Ren'Py", file=sys.stderr)
+        print("correctly, preserving the directory structure.", file=sys.stderr)
         raise
 
     renpy.bootstrap.bootstrap(renpy_base)
diff --git a/tutorial/game/examples.rpy b/tutorial/game/examples.rpy
index 6612f99..216ffd2 100644
--- a/tutorial/game/examples.rpy
+++ b/tutorial/game/examples.rpy
@@ -128,7 +128,7 @@ init python hide:
 
     for fn in files:
 
-        f = file(fn, "r")
+        f = open(fn, "r")
 
         open_examples = set()