def generateAiScripts(self, baseFolder, targetFolder, dcx=True):
     """
     Extract all unique .lua files from .laubnd files so they can be easily accessed.
     """
     SN_RGX = r"[1-9][0-9][0-9][0-9][0-9][0-9]_(battle|logic).lua"
     inputFiles = [
         "m10_00_00_00", "m10_01_00_00", "m10_02_00_00", "m11_00_00_00",
         "m12_00_00_00", "m12_01_00_00", "m13_00_00_00", "m13_01_00_00",
         "m13_02_00_00", "m14_00_00_00", "m14_01_00_00", "m15_00_00_00",
         "m15_01_00_00", "m16_00_00_00", "m17_00_00_00", "m18_00_00_00",
         "m18_01_00_00"
     ]
     tempList = []
     for iFile in inputFiles:
         fullName = baseFolder + iFile + '.luabnd'
         if (dcx):
             fullName += '.dcx'
         with open(fullName, 'rb') as f:
             content = f.read()
             bndcontent = content
             if (dcx):
                 dcxHandler = DCXHandler()
                 bndcontent = dcxHandler.open_dcx(content)
             data = bnd_rebuilder.unpack_bnd(bndcontent)
             for item in data:
                 strName = item[1].decode('shift_jis')
                 match = re.search(SN_RGX, strName)
                 if not (match == None):
                     if not strName in tempList:
                         tempList.append(strName)
                         fileName = match.group(0)
                         if not (os.path.isfile(targetFolder + fileName)):
                             with open(targetFolder + fileName, 'wb') as wf:
                                 wf.write(item[2])
    def Open(self, fname):    #Unused
        print("ffx opened: " + fname)
        self.currentContent = []
        self.lastFFX = 0
        self.lastMDL = 0
        self.lastTPF = 0
        self.existingFiles = []

        with open(fname, 'rb') as f:
            content = f.read()
            self.currentContent = bnd_rebuilder.unpack_bnd(content)
            for i, item in enumerate(self.currentContent):
                strName = item[1].decode('shift_jis')
                if not strName in self.existingFiles:
                    self.existingFiles.append(strName)
                if (item[0] < 100000):
                    if (item[0] > self.lastFFX):
                        self.lastFFX = item[0]
                        self.lastFFXIdx = i + 1
                elif (item[0] < 200000):
                    if (item[0] > self.lastTPF):
                        self.lastTPF = item[0]
                        self.lastTPFIdx = i + 1
                else:
                    if (item[0] > self.lastMDL):
                        self.lastMDL = item[0]
                        self.lastMDLIdx = i + 1
            self.fileOpen = True
    def ExtractEffects(self):    #Unused
        self.CheckDirs()

        inputFiles = ['m10', 'm10_00', 'm10_01', 'm10_02', 'm11', 'm12', 'm12_00', 'm12_01', 'm13', 'm13_00', 'm13_01', 'm13_02', 'm14', 'm14_00', 'm14_01', 'm15', 'm15_00', 'm15_01', 'm16', 'm17', 'm18', 'm18_00', 'm18_01']
        
        tempList = []

        toExtract = []

        for key in self.ffx_ref:
            toExtract += self.ffx_ref[key]

        #print(toExtract)

        for iFile in inputFiles:
            print(iFile)
            
            with open('sfx\\FRPG_SfxBnd_' + iFile + '.ffxbnd', 'rb') as f:
                content = f.read()
                data = bnd_rebuilder.unpack_bnd(content)
                for item in data:
                    strName = item[1].decode('shift_jis')
                    #print(strName)
                    toRemove = -1
                    for i, ffxId in enumerate(toExtract):
                        if (strName == self.ffxString.format(ffxId)):
                            #print("ffx : " + ffxId)
                            with open(self.ffxPath.format(ffxId), "wb") as ffxFile:
                                ffxFile.write(item[2])
                        elif (strName == self.tpfString.format(ffxId)):
                            #print("tex : " + ffxId)
                            with open(self.tpfPath.format(ffxId), "wb") as tpfFile:
                                tpfFile.write(item[2])
                        elif (strName == self.modelString.format(ffxId)):
                            #print("modle : " + ffxId)
                            with open(self.modelPath.format(ffxId), "wb") as flverFile:
                                flverFile.write(item[2])
    def open(self, filename, dcx=True):
        """
        Opens .luabnd file with filename @filename.
        """

        self.luabnd_content = []
        self.luabnd_maxIndex = 0
        self.useDCX = dcx

        fullname = filename
        if (self.useDCX):
            fullname += '.dcx'

        with open(fullname, 'rb') as f:
            content = f.read()
            bndcontent = content
            if (self.useDCX):
                self.dcxhandler = DCXHandler()
                bndcontent = self.dcxhandler.open_dcx(content)
            self.luabnd_content = bnd_rebuilder.unpack_bnd(bndcontent)

        luagnlBytes = b''
        luainfoBytes = b''

        self.luabnd_maxIndex = 0

        for item in self.luabnd_content:
            if (item[0] == 1000000):
                luagnlBytes = item[2]
            elif (item[0] == 1000001):
                luainfoBytes = item[2]
            else:
                if (item[0] > self.luabnd_maxIndex):
                    self.luabnd_maxIndex = item[0]

        return (luagnlBytes, luainfoBytes)
    def export_to_gameparam(self):
        if self.game_version.get() == rngopts.RandOptGameVersion.PTDE:
            paths_to_search = PTDE_GAMEPARAM_PATH_LIST
        elif self.game_version.get() == rngopts.RandOptGameVersion.REMASTERED:
            paths_to_search = DS1R_GAMEPARAM_PATH_LIST
        else:
            paths_to_search = []

        has_gameparam = False
        for filepath in paths_to_search:
            normed_path = os.path.normpath(os.path.join(os.getcwd(), filepath))
            if os.path.isfile(normed_path):
                has_gameparam = True
                gameparam_filepath = normed_path
                gameparambak_filepath = normed_path + ".bak"

        is_remastered = (
            self.game_version.get() == rngopts.RandOptGameVersion.REMASTERED)

        if not has_gameparam:
            self.msg_area.config(state="normal")
            self.msg_area.delete(1.0, "end")
            self.msg_area.insert("end", "\n\n")
            self.msg_area.insert("end", "ERROR", "error_red")
            self.msg_area.insert(
                "end",
                ": GameParam.parambnd[.dcx] is missing or cannot be opened." +
                " Check that this program is in the correct directory and GameParam.parambnd[.dcx] is present and retry.\n\n"
                +
                "Click \"Continue\" to continue in seed-information-only mode, or"
                + " click \"Quit\" to exit.")
            self.msg_area.tag_config("error_red", foreground="red")
            self.msg_area.config(state="disabled")
            self.export_button.config(state="disabled")
            self.lift_msg_area()
        else:
            if is_remastered:
                gp_filename = "GameParam.parambnd.dcx"
            else:
                gp_filename = "GameParam.parambnd"

            with open(gameparam_filepath, "rb") as f:
                content = f.read()
            try:
                if is_remastered:
                    if not dcx_handler.appears_dcx(content):
                        raise ValueError(
                            ".dcx file does not appear to be DCX-compressed.")
                    content = dcx_handler.uncompress_dcx_content(content)
                content_list = bnd_rebuilder.unpack_bnd(content)
            except:
                self.msg_area.config(state="normal")
                self.msg_area.delete(1.0, "end")
                self.msg_area.insert("end", "\n\n")
                self.msg_area.insert("end", "ERROR", "error_red")
                self.msg_area.insert(
                    "end", ": " + gp_filename +
                    " is malformed or corrupted and cannot be" +
                    " parsed to export randomized items. If possible, restore "
                    + gp_filename + " from a backup copy.\n\n" +
                    "Click \"Continue\" to continue in seed-information-only mode, or"
                    + " click \"Quit\" to exit.")
                self.msg_area.tag_config("error_red", foreground="red")
                self.msg_area.config(state="disabled")
                self.export_button.config(state="disabled")
                self.lift_msg_area()
                return

            # Back up GameParam.parambnd if needed.
            if not os.path.isfile(gameparambak_filepath):
                shutil.copy2(gameparam_filepath, gameparambak_filepath)

            if self.is_seed_empty():
                self.get_new_seed()

            for index, (file_id, filepath,
                        filedata) in enumerate(content_list):
                if (filepath ==
                        "N:\FRPG\data\INTERROOT_win32\param\GameParam\CharaInitParam.param"
                        or filepath ==
                        "N:\FRPG\data\INTERROOT_x64\param\GameParam\CharaInitParam.param"
                    ):
                    chr_init_data = filedata

            # TODO: Implement this system correctly by passing chr_init_data
            #  instead of None to preserve externally modified characters (e.g. another mod).
            #  However, we need some way to determine external modifications
            #  compared to data left over from a previous run that changed
            #  ChrInit data.
            (options, randomized_data, rng) = self.randomize_data(None)
            (item_table, randomized_chr_data) = randomized_data
            syncnum = self.get_syncnum_string(rng)

            result_ilp = item_table.build_itemlotparam()
            ilp_binary_export = result_ilp.export_as_binary()
            result_slp = item_table.build_shoplineup()
            slp_binary_export = result_slp.export_as_binary()
            cip_binary_export = randomized_chr_data.export_as_binary()

            for index, (file_id, filepath,
                        filedata) in enumerate(content_list):
                if (filepath ==
                        "N:\FRPG\data\INTERROOT_win32\param\GameParam\ItemLotParam.param"
                        or filepath ==
                        "N:\FRPG\data\INTERROOT_x64\param\GameParam\ItemLotParam.param"
                    ):
                    content_list[index] = (file_id, filepath,
                                           ilp_binary_export)
                if (filepath ==
                        "N:\FRPG\data\INTERROOT_win32\param\GameParam\ShopLineupParam.param"
                        or filepath ==
                        "N:\FRPG\data\INTERROOT_x64\param\GameParam\ShopLineupParam.param"
                    ):
                    content_list[index] = (file_id, filepath,
                                           slp_binary_export)
                if (filepath ==
                        "N:\FRPG\data\INTERROOT_win32\param\GameParam\CharaInitParam.param"
                        or filepath ==
                        "N:\FRPG\data\INTERROOT_x64\param\GameParam\CharaInitParam.param"
                    ):
                    content_list[index] = (file_id, filepath,
                                           cip_binary_export)
            new_content = bnd_rebuilder.repack_bnd(content_list)
            if is_remastered:
                new_content = dcx_handler.compress_dcx_content(new_content)
            with open(gameparam_filepath, "wb") as f:
                f.write(new_content)
            seed_folder = self.export_seed_info(
                (options, randomized_data, rng))

            self.msg_continue_button.lower()
            self.msg_area.config(state="normal")
            self.msg_area.delete(1.0, "end")
            self.msg_area.insert("end", "\n\n")
            self.msg_area.insert("end", "SUCCESS", "yay")
            self.msg_area.insert(
                "end",
                "! " + gp_filename + " has been modified successfully.\n\n" +
                "The information for this seed has been exported in the directory\n\n  "
                + seed_folder + "\n\n")
            self.msg_area.insert(
                "end", "SyncNum: " + syncnum +
                "\n  (When racing, all SyncNums should be equal or settings do not match.)\n\n"
            )
            self.msg_area.insert(
                "end",
                "Click \"Back\" to begin again, or click \"Quit\" to exit.")
            self.msg_area.tag_config("yay", foreground="green")
            self.msg_area.config(state="disabled")
            self.msg_area.lift()
            self.back_button.lift()
            self.msg_quit_button.lift()
    def AddEverythingToCommon(self, useDCX):
        """
        Collects all effects from individual map effect files and adds them to CommonEffects
        """

        inputFiles = ['m10', 'm10_00', 'm10_01', 'm10_02', 'm11', 'm12', 'm12_00', 'm12_01', 'm13', 'm13_00', 'm13_01', 'm13_02', 'm14', 'm14_00', 'm14_01', 'm15', 'm15_00', 'm15_01', 'm16', 'm17', 'm18', 'm18_00', 'm18_01']
        
        MODEL_PATTERN = r'.*s1([0-9][0-9][0-9])[0-9].*'
        TPF_PATTERN = r'.*s1([0-9][0-9][0-9])[0-9].*'
        ENEMY_EFFECT_ID_PATTERN = r'.*00([1-9][0-9][0-9][0-9][0-9]).ffx'
        ENEMY_MODEL_ID_PATTERN = r'.*s([1-9][0-9][0-9][0-9][0-9]).flver'
        ENEMY_TPF_ID_PATTERN = r'.*s([1-9][0-9][0-9][0-9][0-9]).tpf'

        tempList = []
        self.ffx_files.clear()
        for iFile in inputFiles:
            openFileName = 'sfx\\FRPG_SfxBnd_' + iFile + '.ffxbnd'
            if (useDCX):
                openFileName += '.dcx'
            with open(openFileName, 'rb') as f:
                upcontent = f.read()
                content = upcontent
                if (useDCX):
                    dcxh = DCXHandler()
                    content = dcxh.open_dcx(upcontent)
                data = bnd_rebuilder.unpack_bnd(content)
                for item in data:
                    strName = item[1].decode('shift_jis')
                    if not strName in tempList:
                        if (useDCX):
                            enFXMatch = re.match(ENEMY_EFFECT_ID_PATTERN, strName)
                            if (enFXMatch != None):
                                fid = int(enFXMatch.group(1))
                                if (fid < 20001):                   # prev = 60001 below as well in 2 locations
                                    tempList.append(strName)
                                    self.ffx_files.append(item)
                            else:
                                enMDLMatch = re.match(ENEMY_MODEL_ID_PATTERN, strName)
                                if (enMDLMatch != None):
                                    fid = int(enMDLMatch.group(1))
                                    if (fid < 20001):
                                        tempList.append(strName)
                                        self.ffx_files.append(item)
                                else:
                                    enTPFMatch = re.match(ENEMY_TPF_ID_PATTERN, strName)
                                    if (enTPFMatch != None):
                                        fid = int(enTPFMatch.group(1))
                                        if (fid < 20001):
                                            tempList.append(strName)
                                            self.ffx_files.append(item)
                        else:
                            tempList.append(strName)
                            self.ffx_files.append(item)


        existingEffects = []
        lastIndex = 0
        lastIndexTpf = 0
        lastIndexMdl = 0
        ffxEntries = []
        tpfEntries = []
        mdlEntries = []
        openFileName = 'sfx\\FRPG_SfxBnd_CommonEffects.ffxbnd'
        if (useDCX):
            openFileName += '.dcx'

        oldCheckSum = sha256_checksum(openFileName)
        writeSuccessful = True

        with open(openFileName, 'rb') as f:
            upcontent = f.read()
            content = upcontent
            if (useDCX):
                dcxh = DCXHandler()
                content = dcxh.open_dcx(upcontent)
            data = bnd_rebuilder.unpack_bnd(content)
            for item in data:
                strName = item[1].decode('shift_jis')
                if not strName in existingEffects:
                    existingEffects.append(strName)
                if (item[0] < 100000):
                    ffxEntries.append(item)
                    if (item[0] > lastIndex):
                        lastIndex = item[0]
                elif (item[0] < 200000):
                    tpfEntries.append(item)
                    if (item[0] > lastIndexTpf):
                        lastIndexTpf = item[0]
                else:
                    mdlEntries.append(item)
                    if (item[0] > lastIndexMdl):
                        lastIndexMdl = item[0]
            if (not os.path.isfile(openFileName + '.bak')):
                with open(openFileName + '.bak', 'wb') as f2:
                    f2.write(upcontent)

        
        oldLen = len(ffxEntries) + len(tpfEntries) + len(mdlEntries)

        lastIndex += 1
        lastIndexTpf += 1
        lastIndexMdl += 1

        for i, ffx in enumerate(self.ffx_files):
            strName = ffx[1].decode('shift_jis')
            if not strName in existingEffects:
                if (ffx[0] < 100000):
                    newEntry = (lastIndex, ffx[1], ffx[2])
                    lastIndex += 1
                    ffxEntries.append(newEntry)
                elif (ffx[0] < 200000):
                    newEntry = (lastIndexTpf, ffx[1], ffx[2])
                    lastIndexTpf += 1
                    tpfEntries.append(newEntry)
                else:
                    newEntry = (lastIndexMdl, ffx[1], ffx[2])
                    lastIndexMdl += 1
                    mdlEntries.append(newEntry)

        newContent = []
        newContent += ffxEntries
        newContent += tpfEntries
        newContent += mdlEntries
        print("[FFX] Effect data gathered: " + str(len(newContent)))

        if not len(newContent) == oldLen:
            if (useDCX):
                print("[FFX] Saving and recompressing sfx\\FRPG_SfxBnd_CommonEffects.ffxbnd.dcx. This takes quite a while with the Remaster.")
                dcxh = DCXHandler()
                temp = dcxh.open_file('sfx\\FRPG_SfxBnd_CommonEffects.ffxbnd.dcx')
                dcxh.save_dcx('sfx\\FRPG_SfxBnd_CommonEffects.ffxbnd.dcx', bnd_rebuilder.repack_bnd(newContent))
            else:
                print("[FFX] Saving sfx\\FRPG_SfxBnd_CommonEffects.ffxbnd.")
                with open('sfx\\FRPG_SfxBnd_CommonEffects.ffxbnd', 'wb') as f:
                    f.write(bnd_rebuilder.repack_bnd(newContent))

            newCheckSum = sha256_checksum(openFileName)

            if (oldCheckSum == newCheckSum):
                writeSuccessful = False
        
            print('[FFX] sfx\\FRPG_SfxBnd_CommonEffects.ffxbnd saved')

        if not useDCX:
            for iFile in inputFiles:
                data = []
                with open('sfx\\FRPG_SfxBnd_' + iFile + '.ffxbnd', 'rb') as f:
                    content = f.read()
                    data = bnd_rebuilder.unpack_bnd(content)
                    if not (os.path.isfile('sfx\\FRPG_SfxBnd_' + iFile + '.ffxbnd.bak')):
                        with open('sfx\\FRPG_SfxBnd_' + iFile + '.ffxbnd.bak', 'wb') as bakf:
                            bakf.write(content)
                with open('sfx\\FRPG_SfxBnd_' + iFile + '.ffxbnd', 'wb') as sf:
                    sf.write(bnd_rebuilder.repack_bnd(data[:1]))

            print('[FFX] Clean-up complete')
        else:
            print('[FFX] Ignored cleanup (REMASTERED Version being used)')

        print("[FFX] Done")
        return writeSuccessful