class AnagramFile():
  def __init__(self, filename = None):
    self.filename = filename
    
    self.type           = None
    self.solution_index = -1
    self.solution       = None
    self.extra_index    = -1
    self.extra          = None
    
    self.__unknown      = None
    
    self.easy           = None
    self.normal         = None
    self.hard           = None
    
    self.easy_orig      = None
    self.normal_orig    = None
    self.hard_orig      = None
    
    if not filename == None:
      self.load(filename)
  
  def load(self, filename):
    if not os.path.isfile(filename):
      self.filename = None
      return
    
    dat_file = ConstBitStream(filename = self.filename)
    
    type = dat_file.read(16)
    
    if not type == DEMO_FLAG and not type == FULL_FLAG:
      raise Exception("Invalid Anagram", "Invalid anagram type.")
    
    if type == DEMO_FLAG:
      type = ANAGRAM_TYPE.Demo
    else:
      type = ANAGRAM_TYPE.Full
    
    num_letters = dat_file.read('uintle:16')
    magic       = dat_file.read(16)
    
    if not magic == ANAGRAM_MAGIC:
      raise Exception("Invalid Anagram", "Invalid anagram magic.")
    
    solution_index = dat_file.read('uintle:16')
    extra_index    = dat_file.read('uintle:16')
    unknown        = dat_file.read(UNKNOWN_LENGTH[type] * 8)
    
    easy   = dat_file.read(num_letters * 2 * 8)
    normal = dat_file.read(num_letters * 2 * 8)
    hard   = None
    
    if type == ANAGRAM_TYPE.Full:
      hard = dat_file.read(num_letters * 2 * 8)
    
    easy_orig    = None
    normal_orig  = None
    hard_orig    = None
    
    if dat_file.pos >= dat_file.len:
      # If we don't have more data, then consider this untranslated.
      # Therefore, the data we collected becomes the data for the original anagram.
      easy_orig    = easy
      normal_orig  = normal
      hard_orig    = hard
      
      num_letters  = 0
      easy         = None
      normal       = None
      hard         = None
    else:
      # If we DO have more data, consider this translated and grab the
      # info about the untranslated anagram. This data is not used by the game,
      # only the translators.
      letters_orig = dat_file.read('uintle:16')
      easy_orig    = dat_file.read(letters_orig * 2 * 8)
      normal_orig  = dat_file.read(letters_orig * 2 * 8)
      if type == ANAGRAM_TYPE.Full:
        hard_orig  = dat_file.read(letters_orig * 2 * 8)
    
    ##########################################################
    ### Now convert all this into a useful format.
    ##########################################################
    base_dir = os.path.dirname(self.filename)
    
    self.type           = type
    self.solution_index = solution_index
    self.solution       = ScriptFile(os.path.join(base_dir, ANAGRAM_DIR, "%04d.txt" % self.solution_index))
    self.extra_index    = extra_index
    self.extra          = ScriptFile(os.path.join(base_dir, ANAGRAM_DIR, "%04d.txt" % self.extra_index))
    
    self.__unknown      = unknown
    
    self.easy           = self.parse_shown(easy)
    self.normal         = self.parse_shown(normal)
    self.hard           = self.parse_shown(hard)
    
    self.easy_orig      = self.parse_shown(easy_orig)
    self.normal_orig    = self.parse_shown(normal_orig)
    self.hard_orig      = self.parse_shown(hard_orig)
  
  ##############################################################################
  ### @fn    pack()
  ### @desc  Converts all the data into the anagram file format.
  ### @param for_game -- Whether to include the original, untranslated data.
  ###                    True = exclude untranslated, since we don't need it.
  ##############################################################################
  def pack(self, for_game = False):
    
    is_translated = False
    
    if not self.solution[common.editor_config.lang_trans] == "":
      is_translated = True
    
    # SAVE!
    output = BitStream()
    
    # Type flag
    if self.type == ANAGRAM_TYPE.Demo:
      output += DEMO_FLAG
    else:
      output += FULL_FLAG
    
    # Number of letters
    if is_translated:
      output += ConstBitStream(uintle = len(self.solution[common.editor_config.lang_trans]), length = 16)
    else:
      output += ConstBitStream(uintle = len(self.solution[common.editor_config.lang_orig]), length = 16)
    
    # Magic
    output += ANAGRAM_MAGIC
    
    # File indexes
    output += ConstBitStream(uintle = self.solution_index, length = 16)
    output += ConstBitStream(uintle = self.extra_index, length = 16)
    
    # Unknown
    output += self.__unknown
    
    # Shown/unshown
    if is_translated:
      num_letters = len(self.solution[common.editor_config.lang_trans])
      output += self.pack_shown(self.easy, num_letters)
      output += self.pack_shown(self.normal, num_letters)
      
      if self.type == ANAGRAM_TYPE.Full:
        output += self.pack_shown(self.hard, num_letters)
      
      if not for_game:
        num_letters = len(self.solution[common.editor_config.lang_orig])
        output += ConstBitStream(uintle = num_letters, length = 16)
    
    if not is_translated or not for_game:
      # This shows up either way.
      num_letters = len(self.solution[common.editor_config.lang_orig])
      output += self.pack_shown(self.easy_orig, num_letters)
      output += self.pack_shown(self.normal_orig, num_letters)
      
      if self.type == ANAGRAM_TYPE.Full:
        output += self.pack_shown(self.hard_orig, num_letters)
    
    return output
  
  def save(self, filename = None):
    if filename == None:
      if self.filename == None:
        return
      else:
        filename = self.filename
    
    output = self.pack(for_game = False)
    
    f = open(filename, "wb")
    output.tofile(f)
    f.close()
    
    dirname = os.path.dirname(filename)
    dirname = os.path.join(dirname, ANAGRAM_DIR)
    
    if not os.path.isdir(dirname):
      os.makedirs(dirname)
    
    self.solution.save(os.path.join(dirname, "%04d.txt" % self.solution_index))
    self.extra.save   (os.path.join(dirname, "%04d.txt" % self.extra_index))
  
  def backup(self):
    
    backup_loc = time.strftime("%Y.%m.%d_%H.%M.%S_ANAGRAM")
    
    dirname = common.editor_config.backup_dir
    dirname = os.path.join(dirname, backup_loc)
    
    # Make sure we have a place to put it.
    if not os.path.isdir(dirname):
      os.makedirs(dirname)
    
    basename = os.path.basename(self.filename)
    target = os.path.join(dirname, basename)
    
    shutil.copy(self.filename, target)
    
    # Now for the text files themselves.
    source_dir = os.path.dirname(self.filename)
    source_dir = os.path.join(source_dir, ANAGRAM_DIR)
    
    solution_source = os.path.join(source_dir, "%04d.txt" % self.solution_index)
    extra_source    = os.path.join(source_dir, "%04d.txt" % self.extra_index)
    
    target_dir = os.path.join(dirname, ANAGRAM_DIR)
    
    # Make sure we have a place to put it.
    if not os.path.isdir(target_dir):
      os.makedirs(target_dir)
    
    solution_target = os.path.join(target_dir, "%04d.txt" % self.solution_index)
    extra_target    = os.path.join(target_dir, "%04d.txt" % self.extra_index)
    
    shutil.copy(solution_source, solution_target)
    shutil.copy(extra_source, extra_target)
  
  def parse_shown(self, data):
    if data == None:
      return None
    
    # Two bytes per letter.
    num_letters = (data.len / 16)
    
    shown = []
    
    for i in range(1, num_letters + 1):
      letter = data.read(16)
      
      if letter == LETTER_VISIBLE:
        shown.append(i)
    
    return shown
  
  def pack_shown(self, shown, num_letters):
    data = BitStream()
    
    if shown == None:
      shown = []
    
    for i in range(1, num_letters + 1):
      if i in shown:
        data += LETTER_VISIBLE
      else:
        data += LETTER_HIDDEN
    
    return data
 def load(self, filename):
   if not os.path.isfile(filename):
     self.filename = None
     return
   
   dat_file = ConstBitStream(filename = self.filename)
   
   type = dat_file.read(16)
   
   if not type == DEMO_FLAG and not type == FULL_FLAG:
     raise Exception("Invalid Anagram", "Invalid anagram type.")
   
   if type == DEMO_FLAG:
     type = ANAGRAM_TYPE.Demo
   else:
     type = ANAGRAM_TYPE.Full
   
   num_letters = dat_file.read('uintle:16')
   magic       = dat_file.read(16)
   
   if not magic == ANAGRAM_MAGIC:
     raise Exception("Invalid Anagram", "Invalid anagram magic.")
   
   solution_index = dat_file.read('uintle:16')
   extra_index    = dat_file.read('uintle:16')
   unknown        = dat_file.read(UNKNOWN_LENGTH[type] * 8)
   
   easy   = dat_file.read(num_letters * 2 * 8)
   normal = dat_file.read(num_letters * 2 * 8)
   hard   = None
   
   if type == ANAGRAM_TYPE.Full:
     hard = dat_file.read(num_letters * 2 * 8)
   
   easy_orig    = None
   normal_orig  = None
   hard_orig    = None
   
   if dat_file.pos >= dat_file.len:
     # If we don't have more data, then consider this untranslated.
     # Therefore, the data we collected becomes the data for the original anagram.
     easy_orig    = easy
     normal_orig  = normal
     hard_orig    = hard
     
     num_letters  = 0
     easy         = None
     normal       = None
     hard         = None
   else:
     # If we DO have more data, consider this translated and grab the
     # info about the untranslated anagram. This data is not used by the game,
     # only the translators.
     letters_orig = dat_file.read('uintle:16')
     easy_orig    = dat_file.read(letters_orig * 2 * 8)
     normal_orig  = dat_file.read(letters_orig * 2 * 8)
     if type == ANAGRAM_TYPE.Full:
       hard_orig  = dat_file.read(letters_orig * 2 * 8)
   
   ##########################################################
   ### Now convert all this into a useful format.
   ##########################################################
   base_dir = os.path.dirname(self.filename)
   
   self.type           = type
   self.solution_index = solution_index
   self.solution       = ScriptFile(os.path.join(base_dir, ANAGRAM_DIR, "%04d.txt" % self.solution_index))
   self.extra_index    = extra_index
   self.extra          = ScriptFile(os.path.join(base_dir, ANAGRAM_DIR, "%04d.txt" % self.extra_index))
   
   self.__unknown      = unknown
   
   self.easy           = self.parse_shown(easy)
   self.normal         = self.parse_shown(normal)
   self.hard           = self.parse_shown(hard)
   
   self.easy_orig      = self.parse_shown(easy_orig)
   self.normal_orig    = self.parse_shown(normal_orig)
   self.hard_orig      = self.parse_shown(hard_orig)
    def changedSelection(self, current, prev):
        if current == None or current.childCount() != 0:
            return

        file = common.qt_to_unicode(current.text(0))
        path = tree.tree_item_to_path(current.parent())
        self.setWindowTitle("%s - %s" %
                            (self.menu_name, os.path.join(path, file)))
        path = dir_tools.expand_dir(path)

        file = os.path.join(path, file)
        file1 = os.path.join(self.folder1, file)
        file2 = os.path.join(self.folder2, file)

        if not os.path.isfile(file1):
            script1 = ScriptFile()
        else:
            script1 = ScriptFile(file1)

        if not os.path.isfile(file2):
            script2 = ScriptFile()
        else:
            script2 = ScriptFile(file2)

        # So we can loop this shit.
        to_diff = [
            # Text 1              Text 2              Text Box 1              Text Box 2
            (script1[common.editor_config.lang_trans],
             script2[common.editor_config.lang_trans], self.ui.txtTranslated1,
             self.ui.txtTranslated2),
            (script1[common.editor_config.lang_orig],
             script2[common.editor_config.lang_orig], self.ui.txtOriginal1,
             self.ui.txtOriginal2),
            (script1.comments, script2.comments, self.ui.txtComments1,
             self.ui.txtComments2),
        ]

        # Save us a little bit of time recalculating.
        if file in self.saved_diffs:
            diffs = self.saved_diffs[file]
        else:
            diffs = [None] * len(to_diff)

        for i, (text1, text2, box1, box2) in enumerate(to_diff):

            if diffs[i] == None:
                diffs[i] = DIFFER.diff_main(text1, text2)
                DIFFER.diff_cleanupSemantic(diffs[i])

            box1.setPlainText(text1)
            box2.setPlainText(text2)

            highlight1, highlight2 = parse_diffs(diffs[i])

            cursor1 = box1.textCursor()
            cursor2 = box2.textCursor()

            cursor1.select(QTextCursor.Document)
            cursor2.select(QTextCursor.Document)
            cursor1.setCharFormat(self.format_plain)
            cursor2.setCharFormat(self.format_plain)

            cursor1.movePosition(QTextCursor.Start)
            cursor2.movePosition(QTextCursor.Start)

            for pos, length in highlight1:
                cursor1.setPosition(pos, QTextCursor.MoveAnchor)
                cursor1.setPosition(pos + length, QTextCursor.KeepAnchor)
                cursor1.setCharFormat(self.format1)

            cursor1.movePosition(QTextCursor.Start)

            for pos, length in highlight2:
                cursor2.setPosition(pos, QTextCursor.MoveAnchor)
                cursor2.setPosition(pos + length, QTextCursor.KeepAnchor)
                cursor2.setCharFormat(self.format2)

            cursor2.movePosition(QTextCursor.Start)

            box1.setTextCursor(cursor1)
            box2.setTextCursor(cursor2)

        # for i, (text1, text2, box1, box2) in enumerate(to_diff):
        self.saved_diffs[file] = diffs
    def load_dir(self, directory, umdimage="umdimage"):

        self.script_files = []
        self.directory = directory
        self.wrd = None
        self.wrd_file = None
        self.py_file = None

        base_name = directory
        directory, wrd_file = dir_tools.parse_dir(directory, umdimage)

        if directory == None:
            self.directory = ""
            raise Exception("Directory \"" + base_name + "\" not found.")

        full_dir = os.path.join(umdimage, directory)

        scene_info = []

        if not wrd_file == None and os.path.isfile(wrd_file):

            # Even of the first directory existed and we have a .wrd file,
            # it's possible there aren't actually any text files here.
            if not os.path.isdir(full_dir):
                raise Exception("There are no text files in \"" + directory +
                                "\".")

            self.wrd = WrdFile()

            py_file = os.path.splitext(wrd_file)[0] + ".py"

            if os.path.isfile(py_file):
                try:
                    self.wrd.load_python(py_file)
                except:
                    _LOGGER.warning(
                        "%s failed to load. Parsing wrd file instead. Exception info:\n%s"
                        % (py_file, traceback.format_exc()))
                    self.wrd.load_bin(wrd_file)
                else:
                    # If we succeeded in loading the python file, compile it to binary.
                    # _LOGGER.info("%s loaded successfully. Compiling to binary." % py_file)
                    # self.wrd.save_bin(wrd_file)
                    _LOGGER.info("%s loaded successfully." % py_file)

            else:
                _LOGGER.info("Decompiled wrd file not found. Generating %s" %
                             py_file)
                self.wrd.load_bin(wrd_file)
                self.wrd.save_python(py_file)

            scene_info = self.wrd.to_scene_info()
            self.wrd_file = wrd_file
            self.py_file = py_file

        else:
            scene_info = None
            self.wrd = None
            self.wrd_file = None

        self.script_files = []
        if scene_info == None:
            text_files = list_all_files(full_dir)
            for file in text_files:
                self.script_files.append(ScriptFile(file))

        else:
            # Get our files in the order listed by the wrd.
            for info in scene_info:
                filename = os.path.join(full_dir, "%04d.txt" % info.file_id)
                script_file = ScriptFile(filename, info)

                if script_file.filename == None:
                    _LOGGER.warning(
                        "File %s referenced by %s does not exist." %
                        (filename, wrd_file))
                    continue

                self.script_files.append(script_file)

        chapter, scene, room, mode = common.get_dir_info(base_name)

        for file in self.script_files:
            if file.scene_info.chapter == -1: file.scene_info.chapter = chapter
            if file.scene_info.scene == -1: file.scene_info.scene = scene
            if file.scene_info.room == -1: file.scene_info.room = room
            if file.scene_info.mode == None: file.scene_info.mode = mode
Esempio n. 5
0
    def load_dir(self, directory, base_dir="data01"):

        if not directory:
            return

        # directory, wrd_file = dir_tools.parse_dir(directory, base_dir)
        # Only expands if necessary.
        full_dir = dir_tools.expand_script_pak(directory)

        directory1 = normalize(directory)
        match = RE_FONT_PAK.match(directory1)
        if match:
            full_dir = os.path.join(base_dir, SCRIPT_BIN_DIR, full_dir)

    #     if not os.path.isdir(full_dir):
    #       full_dir = os.path.join(base_dir, SCRIPT_BIN_DIR, directory)
        else:
            full_dir = os.path.join(base_dir, SCRIPT_DIR, full_dir)

        if not os.path.isdir(full_dir):
            raise Exception("Directory \"" + full_dir + "\" not found.")

        self.script_files = []
        self.directory = directory
        self.wrd = None
        self.wrd_file = None
        self.py_file = None

        scene_info = []
        wrd_file = os.path.join(full_dir,
                                os.path.splitext(directory)[0] + ".scp.wrd")

        if os.path.isfile(wrd_file):

            self.wrd = WrdFile()
            py_file = os.path.splitext(wrd_file)[0] + ".py"

            if os.path.isfile(py_file):
                try:
                    self.wrd.load_python(py_file)
                except:
                    _LOGGER.warning(
                        "%s failed to load. Parsing wrd file instead. Exception info:\n%s"
                        % (py_file, traceback.format_exc()))
                    self.wrd.load_bin(wrd_file)
                else:
                    # If we succeeded in loading the python file, compile it to binary.
                    # _LOGGER.info("%s loaded successfully. Compiling to binary." % py_file)
                    # self.wrd.save_bin(wrd_file)
                    _LOGGER.info("%s loaded successfully." % py_file)

            else:
                _LOGGER.info("Decompiled wrd file not found. Generating %s" %
                             py_file)
                self.wrd.load_bin(wrd_file)
                self.wrd.save_python(py_file)

            scene_info = self.wrd.to_scene_info()
            self.wrd_file = wrd_file
            self.py_file = py_file

        else:
            scene_info = None
            self.wrd = None
            self.wrd_file = None
            self.py_file = None

        self.script_files = []
        if scene_info == None:
            text_files = [
                filename for filename in os.listdir(full_dir)
                if os.path.splitext(filename)[1].lower() == ".txt"
            ]
            for filename in text_files:
                self.script_files.append(
                    ScriptFile(os.path.join(full_dir, filename)))

        else:
            # Get our files in the order listed by the wrd.
            for info in scene_info:
                if info.file_id == None:
                    script_file = ScriptJump(info)

                else:
                    filename = os.path.join(full_dir,
                                            "%04d.txt" % info.file_id)
                    script_file = ScriptFile(filename, info)

                    if script_file.filename == None:
                        _LOGGER.warning(
                            "File %s referenced by %s does not exist." %
                            (filename, wrd_file))
                        continue

                self.script_files.append(script_file)

        chapter, scene, room, mode = common.get_dir_info(directory)

        for file in self.script_files:
            if file.scene_info.chapter == -1: file.scene_info.chapter = chapter
            if file.scene_info.scene == -1: file.scene_info.scene = scene
            if file.scene_info.room == -1: file.scene_info.room = room
            if file.scene_info.mode == None: file.scene_info.mode = mode
    def load(self, filename):
        if not os.path.isfile(filename):
            self.filename = None
            return

        dat_file = ConstBitStream(filename=self.filename)

        type = dat_file.read(16)

        if not type == DEMO_FLAG and not type == FULL_FLAG:
            raise Exception("Invalid Anagram", "Invalid anagram type.")

        if type == DEMO_FLAG:
            type = ANAGRAM_TYPE.Demo
        else:
            type = ANAGRAM_TYPE.Full

        num_letters = dat_file.read('uintle:16')
        magic = dat_file.read(16)

        if not magic == ANAGRAM_MAGIC:
            raise Exception("Invalid Anagram", "Invalid anagram magic.")

        solution_index = dat_file.read('uintle:16')
        extra_index = dat_file.read('uintle:16')
        unknown = dat_file.read(UNKNOWN_LENGTH[type] * 8)

        easy = dat_file.read(num_letters * 2 * 8)
        normal = dat_file.read(num_letters * 2 * 8)
        hard = None

        if type == ANAGRAM_TYPE.Full:
            hard = dat_file.read(num_letters * 2 * 8)

        easy_orig = None
        normal_orig = None
        hard_orig = None

        if dat_file.pos >= dat_file.len:
            # If we don't have more data, then consider this untranslated.
            # Therefore, the data we collected becomes the data for the original anagram.
            easy_orig = easy
            normal_orig = normal
            hard_orig = hard

            num_letters = 0
            easy = None
            normal = None
            hard = None
        else:
            # If we DO have more data, consider this translated and grab the
            # info about the untranslated anagram. This data is not used by the game,
            # only the translators.
            letters_orig = dat_file.read('uintle:16')
            easy_orig = dat_file.read(letters_orig * 2 * 8)
            normal_orig = dat_file.read(letters_orig * 2 * 8)
            if type == ANAGRAM_TYPE.Full:
                hard_orig = dat_file.read(letters_orig * 2 * 8)

        ##########################################################
        ### Now convert all this into a useful format.
        ##########################################################
        base_dir = os.path.dirname(self.filename)

        self.type = type
        self.solution_index = solution_index
        self.solution = ScriptFile(
            os.path.join(base_dir, ANAGRAM_DIR,
                         "%04d.txt" % self.solution_index))
        self.extra_index = extra_index
        self.extra = ScriptFile(
            os.path.join(base_dir, ANAGRAM_DIR, "%04d.txt" % self.extra_index))

        self.__unknown = unknown

        self.easy = self.parse_shown(easy)
        self.normal = self.parse_shown(normal)
        self.hard = self.parse_shown(hard)

        self.easy_orig = self.parse_shown(easy_orig)
        self.normal_orig = self.parse_shown(normal_orig)
        self.hard_orig = self.parse_shown(hard_orig)
class AnagramFile():
    def __init__(self, filename=None):
        self.filename = filename

        self.type = None
        self.solution_index = -1
        self.solution = None
        self.extra_index = -1
        self.extra = None

        self.__unknown = None

        self.easy = None
        self.normal = None
        self.hard = None

        self.easy_orig = None
        self.normal_orig = None
        self.hard_orig = None

        if not filename == None:
            self.load(filename)

    def load(self, filename):
        if not os.path.isfile(filename):
            self.filename = None
            return

        dat_file = ConstBitStream(filename=self.filename)

        type = dat_file.read(16)

        if not type == DEMO_FLAG and not type == FULL_FLAG:
            raise Exception("Invalid Anagram", "Invalid anagram type.")

        if type == DEMO_FLAG:
            type = ANAGRAM_TYPE.Demo
        else:
            type = ANAGRAM_TYPE.Full

        num_letters = dat_file.read('uintle:16')
        magic = dat_file.read(16)

        if not magic == ANAGRAM_MAGIC:
            raise Exception("Invalid Anagram", "Invalid anagram magic.")

        solution_index = dat_file.read('uintle:16')
        extra_index = dat_file.read('uintle:16')
        unknown = dat_file.read(UNKNOWN_LENGTH[type] * 8)

        easy = dat_file.read(num_letters * 2 * 8)
        normal = dat_file.read(num_letters * 2 * 8)
        hard = None

        if type == ANAGRAM_TYPE.Full:
            hard = dat_file.read(num_letters * 2 * 8)

        easy_orig = None
        normal_orig = None
        hard_orig = None

        if dat_file.pos >= dat_file.len:
            # If we don't have more data, then consider this untranslated.
            # Therefore, the data we collected becomes the data for the original anagram.
            easy_orig = easy
            normal_orig = normal
            hard_orig = hard

            num_letters = 0
            easy = None
            normal = None
            hard = None
        else:
            # If we DO have more data, consider this translated and grab the
            # info about the untranslated anagram. This data is not used by the game,
            # only the translators.
            letters_orig = dat_file.read('uintle:16')
            easy_orig = dat_file.read(letters_orig * 2 * 8)
            normal_orig = dat_file.read(letters_orig * 2 * 8)
            if type == ANAGRAM_TYPE.Full:
                hard_orig = dat_file.read(letters_orig * 2 * 8)

        ##########################################################
        ### Now convert all this into a useful format.
        ##########################################################
        base_dir = os.path.dirname(self.filename)

        self.type = type
        self.solution_index = solution_index
        self.solution = ScriptFile(
            os.path.join(base_dir, ANAGRAM_DIR,
                         "%04d.txt" % self.solution_index))
        self.extra_index = extra_index
        self.extra = ScriptFile(
            os.path.join(base_dir, ANAGRAM_DIR, "%04d.txt" % self.extra_index))

        self.__unknown = unknown

        self.easy = self.parse_shown(easy)
        self.normal = self.parse_shown(normal)
        self.hard = self.parse_shown(hard)

        self.easy_orig = self.parse_shown(easy_orig)
        self.normal_orig = self.parse_shown(normal_orig)
        self.hard_orig = self.parse_shown(hard_orig)

    ##############################################################################
    ### @fn    pack()
    ### @desc  Converts all the data into the anagram file format.
    ### @param for_game -- Whether to include the original, untranslated data.
    ###                    True = exclude untranslated, since we don't need it.
    ##############################################################################
    def pack(self, for_game=False):

        is_translated = False

        if not self.solution[common.editor_config.lang_trans] == "":
            is_translated = True

        # SAVE!
        output = BitStream()

        # Type flag
        if self.type == ANAGRAM_TYPE.Demo:
            output += DEMO_FLAG
        else:
            output += FULL_FLAG

        # Number of letters
        if is_translated:
            output += ConstBitStream(uintle=len(
                self.solution[common.editor_config.lang_trans]),
                                     length=16)
        else:
            output += ConstBitStream(uintle=len(
                self.solution[common.editor_config.lang_orig]),
                                     length=16)

        # Magic
        output += ANAGRAM_MAGIC

        # File indexes
        output += ConstBitStream(uintle=self.solution_index, length=16)
        output += ConstBitStream(uintle=self.extra_index, length=16)

        # Unknown
        output += self.__unknown

        # Shown/unshown
        if is_translated:
            num_letters = len(self.solution[common.editor_config.lang_trans])
            output += self.pack_shown(self.easy, num_letters)
            output += self.pack_shown(self.normal, num_letters)

            if self.type == ANAGRAM_TYPE.Full:
                output += self.pack_shown(self.hard, num_letters)

            if not for_game:
                num_letters = len(
                    self.solution[common.editor_config.lang_orig])
                output += ConstBitStream(uintle=num_letters, length=16)

        if not is_translated or not for_game:
            # This shows up either way.
            num_letters = len(self.solution[common.editor_config.lang_orig])
            output += self.pack_shown(self.easy_orig, num_letters)
            output += self.pack_shown(self.normal_orig, num_letters)

            if self.type == ANAGRAM_TYPE.Full:
                output += self.pack_shown(self.hard_orig, num_letters)

        return output

    def save(self, filename=None):
        if filename == None:
            if self.filename == None:
                return
            else:
                filename = self.filename

        output = self.pack(for_game=False)

        f = open(filename, "wb")
        output.tofile(f)
        f.close()

        dirname = os.path.dirname(filename)
        dirname = os.path.join(dirname, ANAGRAM_DIR)

        if not os.path.isdir(dirname):
            os.makedirs(dirname)

        self.solution.save(
            os.path.join(dirname, "%04d.txt" % self.solution_index))
        self.extra.save(os.path.join(dirname, "%04d.txt" % self.extra_index))

    def backup(self):

        backup_loc = time.strftime("%Y.%m.%d_%H.%M.%S_ANAGRAM")

        dirname = common.editor_config.backup_dir
        dirname = os.path.join(dirname, backup_loc)

        # Make sure we have a place to put it.
        if not os.path.isdir(dirname):
            os.makedirs(dirname)

        basename = os.path.basename(self.filename)
        target = os.path.join(dirname, basename)

        shutil.copy(self.filename, target)

        # Now for the text files themselves.
        source_dir = os.path.dirname(self.filename)
        source_dir = os.path.join(source_dir, ANAGRAM_DIR)

        solution_source = os.path.join(source_dir,
                                       "%04d.txt" % self.solution_index)
        extra_source = os.path.join(source_dir, "%04d.txt" % self.extra_index)

        target_dir = os.path.join(dirname, ANAGRAM_DIR)

        # Make sure we have a place to put it.
        if not os.path.isdir(target_dir):
            os.makedirs(target_dir)

        solution_target = os.path.join(target_dir,
                                       "%04d.txt" % self.solution_index)
        extra_target = os.path.join(target_dir, "%04d.txt" % self.extra_index)

        shutil.copy(solution_source, solution_target)
        shutil.copy(extra_source, extra_target)

    def parse_shown(self, data):
        if data == None:
            return None

        # Two bytes per letter.
        num_letters = (data.len / 16)

        shown = []

        for i in range(1, num_letters + 1):
            letter = data.read(16)

            if letter == LETTER_VISIBLE:
                shown.append(i)

        return shown

    def pack_shown(self, shown, num_letters):
        data = BitStream()

        if shown == None:
            shown = []

        for i in range(1, num_letters + 1):
            if i in shown:
                data += LETTER_VISIBLE
            else:
                data += LETTER_HIDDEN

        return data