Ejemplo n.º 1
0
def emevd_file_content_to_event_list(file_content, filetype_override=None):
    """Parses the binary data in file_content as a .emevd file. Returns
    the list of Events in the file. Raises a ValueError if file_content
    appears to be malformed or un-parseable.
    
    Filetype is (mostly) automatically detected using header information,
    but can be overridden by passing a EVD_Filetype value as filetype_override.
    """

    filetype = filetype_override
    event_list = []

    should_dcx = False
    if dcx_handler.appears_dcx(file_content):
        tmp = dcx_handler.uncompress_dcx_content(file_content)
        file_content = tmp
        should_dcx = True

    master_offset = 0

    master_offset = consume_byte(file_content, master_offset, '\x45', 1)
    master_offset = consume_byte(file_content, master_offset, '\x56', 1)
    master_offset = consume_byte(file_content, master_offset, '\x44', 1)
    master_offset = consume_byte(file_content, master_offset, '\x00', 1)

    (version_part_1, version_part_2) = struct.unpack_from("<II",
                                                          file_content,
                                                          offset=master_offset)
    master_offset += struct.calcsize("<II")

    # Determine filetype from version bytes, if not overriden.
    if filetype == None:
        if version_part_1 == 0x00000000 and version_part_2 == 0x000000CC:
            filetype = EVD_FileType.DARK_SOULS_1
        elif version_part_1 == 0x0000FF00 and version_part_2 == 0x000000CC:
            # We cannot tell the difference between BB and DS3Test from the header,
            #  so default to BB unless the user forces an override.
            filetype = EVD_FileType.BLOODBORNE
        elif version_part_1 == 0x0001FF00 and version_part_2 == 0x000000CD:
            filetype = EVD_FileType.DARK_SOULS_3
        else:
            raise ValueError(("Unrecognized version bytes %08x %08x." +
                              "Could not determine EVD version.") %
                             (version_part_1, version_part_2))

    (file_size, ) = struct.unpack_from("<I",
                                       file_content,
                                       offset=master_offset)
    master_offset += struct.calcsize("<I")

    header_pair_format_string = "<II"
    if filetype == EVD_FileType.DARK_SOULS_1:
        header_pair_format_string = "<II"
    elif (filetype == EVD_FileType.BLOODBORNE
          or filetype == EVD_FileType.DARK_SOULS_3_TEST
          or filetype == EVD_FileType.DARK_SOULS_3):
        header_pair_format_string = "<QQ"

    (event_count, event_offset) = struct.unpack_from(header_pair_format_string,
                                                     file_content,
                                                     offset=master_offset)
    master_offset += struct.calcsize(header_pair_format_string)
    (instr_count, instr_offset) = struct.unpack_from(header_pair_format_string,
                                                     file_content,
                                                     offset=master_offset)
    master_offset += struct.calcsize(header_pair_format_string)

    # This table is unused in known formats, so it always has 0 count and dummy offset.
    (_, _) = struct.unpack_from(header_pair_format_string,
                                file_content,
                                offset=master_offset)
    master_offset += struct.calcsize(header_pair_format_string)

    (eventlayer_count,
     eventlayer_offset) = struct.unpack_from(header_pair_format_string,
                                             file_content,
                                             offset=master_offset)
    master_offset += struct.calcsize(header_pair_format_string)
    (dynarg_count,
     dynarg_offset) = struct.unpack_from(header_pair_format_string,
                                         file_content,
                                         offset=master_offset)
    master_offset += struct.calcsize(header_pair_format_string)
    (linked_files_count,
     linked_files_offset) = struct.unpack_from(header_pair_format_string,
                                               file_content,
                                               offset=master_offset)
    master_offset += struct.calcsize(header_pair_format_string)
    (fixarg_length,
     fixarg_offset) = struct.unpack_from(header_pair_format_string,
                                         file_content,
                                         offset=master_offset)
    master_offset += struct.calcsize(header_pair_format_string)
    (strings_length,
     strings_offset) = struct.unpack_from(header_pair_format_string,
                                          file_content,
                                          offset=master_offset)
    master_offset += struct.calcsize(header_pair_format_string)

    master_offset = event_offset
    for i in range(event_count):
        event_reset_mode = 0
        if filetype == EVD_FileType.DARK_SOULS_1:
            (event_id, num_of_instr, starting_instr_offset, num_of_dynarg,
             starting_dynarg_offset, event_reset_mode,
             zero) = struct.unpack_from("<IIIIiII",
                                        file_content,
                                        offset=master_offset)
            master_offset += struct.calcsize("<IIIIiII")
        elif filetype == EVD_FileType.BLOODBORNE or filetype == EVD_FileType.DARK_SOULS_3_TEST:
            (event_id, num_of_instr, starting_instr_offset, num_of_dynarg,
             starting_dynarg_offset, event_reset_mode,
             zero) = struct.unpack_from("<QQQQi4xII",
                                        file_content,
                                        offset=master_offset)
            master_offset += struct.calcsize("<QQQQi4xII")
        elif filetype == EVD_FileType.DARK_SOULS_3:
            (event_id, num_of_instr, starting_instr_offset, num_of_dynarg,
             starting_dynarg_offset, event_reset_mode,
             zero) = struct.unpack_from("<QQQQqII",
                                        file_content,
                                        offset=master_offset)
            master_offset += struct.calcsize("<QQQQqII")
        if zero != 0:
            raise ValueError(
                "Event terminator zero was expected for event ID %d, but received %d instead."
                % (event_id, zero))

        offset = instr_offset + starting_instr_offset
        instr_list = []
        for i in range(num_of_instr):
            complain_about_zero = False
            if filetype == EVD_FileType.DARK_SOULS_1:
                (instr_class, instr_index, fixarg_bytes,
                 starting_fixarg_offset, starting_eventlayer_offset,
                 zero) = struct.unpack_from("<IIIiiI",
                                            file_content,
                                            offset=offset)
                if zero != 0:
                    complain_about_zero = True
                offset += struct.calcsize("<IIIiiI")
            elif filetype == EVD_FileType.BLOODBORNE or filetype == EVD_FileType.DARK_SOULS_3_TEST:
                (instr_class, instr_index, fixarg_bytes, starting_fixarg_offset, \
                    starting_eventlayer_offset, zero) = struct.unpack_from("<IIQi4xiI", file_content, offset=offset)
                if zero != 0:
                    complain_about_zero = True
                offset += struct.calcsize("<IIQi4xi4x")
            elif filetype == EVD_FileType.DARK_SOULS_3:
                (instr_class, instr_index, fixarg_bytes,
                 starting_fixarg_offset,
                 starting_eventlayer_offset) = struct.unpack_from(
                     "<IIQi4xq", file_content, offset=offset)
                offset += struct.calcsize("<IIQi4xq")
            if complain_about_zero:
                raise ValueError((
                    "Instruction terminator zero was expected for " +
                    "instruction %d[%03d] of event ID %d, but received %d instead."
                ) % (instr_class, instr_index, event_id, zero))

            # Parse Event Layer
            eventlayer = None
            if starting_eventlayer_offset != -1:
                # Known examples have a very strict format. If something deviates from this, we should look into it.
                if filetype == EVD_FileType.DARK_SOULS_1:
                    # This is a guess, since DS1 never uses the event layer field.
                    (two, eventlayer, zero, neg_one, one) = struct.unpack_from(
                        "<IIIiI",
                        file_content,
                        offset=eventlayer_offset + starting_eventlayer_offset)
                elif (filetype == EVD_FileType.BLOODBORNE
                      or filetype == EVD_FileType.DARK_SOULS_3_TEST
                      or filetype == EVD_FileType.DARK_SOULS_3):
                    # This is partially a guess, since BB/DS3T never uses the event layer field.
                    (two, eventlayer, zero, neg_one, one) = struct.unpack_from(
                        "<IIQqQ",
                        file_content,
                        offset=eventlayer_offset + starting_eventlayer_offset)
                if two != 2:
                    ValueError(
                        "Event Layer @ %08x initializer 2 was expected but received %d instead."
                        %
                        (eventlayer_offset + starting_eventlayer_offset, two))
                if zero != 0:
                    raise ValueError(
                        "Event Layer @ %08x terminator zero was expected but received %d instead."
                        %
                        (eventlayer_offset + starting_eventlayer_offset, zero))
                if neg_one != -1:
                    raise ValueError(
                        "Event Layer @ %08x terminator -1 was expected but received %d instead."
                        % (eventlayer_offset + starting_eventlayer_offset,
                           neg_one))
                if one != 1:
                    raise ValueError(
                        "Event Layer @ %08x terminator 1 was expected but received %d instead."
                        %
                        (eventlayer_offset + starting_eventlayer_offset, one))

            (instr_format_string, instr_arg_list) = unpack_arg_array(
                file_content, instr_class, instr_index,
                fixarg_offset + starting_fixarg_offset, fixarg_bytes, filetype)

            instr = emevd_handler.Instruction(instr_class, instr_index,
                                              instr_format_string,
                                              instr_arg_list, [], eventlayer)
            instr_list.append(instr)

        offset = dynarg_offset + starting_dynarg_offset
        for i in range(num_of_dynarg):
            if filetype == EVD_FileType.DARK_SOULS_1:
                (instr_index, destination_starting_byte, source_starting_byte, \
                    num_of_bytes, zero) = struct.unpack_from("<IIIII", file_content, offset=offset)
                offset += struct.calcsize("<IIIII")
                if zero != 0:
                    raise ValueError(
                        ("Parameter replacement #%d terminator zero" +
                         " was expected but received %d instead.") % (i, zero))
            elif (filetype == EVD_FileType.BLOODBORNE
                  or filetype == EVD_FileType.DARK_SOULS_3_TEST
                  or filetype == EVD_FileType.DARK_SOULS_3):
                (instr_index, destination_starting_byte, source_starting_byte,
                 num_of_bytes) = struct.unpack_from("<QQQQ",
                                                    file_content,
                                                    offset=offset)
                offset += struct.calcsize("<QQQQ")
            param = emevd_handler.ParameterReplacement(
                instr_index, destination_starting_byte, source_starting_byte,
                num_of_bytes)
            instr_list[instr_index].append_parameter_replacement(param)

        event = emevd_handler.Event(event_id, event_reset_mode, instr_list)
        event_list.append(event)

    strings = b""
    if strings_length != 0:
        strings = file_content[strings_offset:strings_offset + strings_length]

    master_offset = linked_files_offset
    linked_files_list = []
    for i in range(linked_files_count):
        if filetype == EVD_FileType.DARK_SOULS_1:
            # DS1 never uses linked files, but why not attempt to support it?
            (linked_files_name_offset, ) = struct.unpack_from(
                "<I", file_content, offset=master_offset)
            linked_files_list.append(linked_files_name_offset)
        elif (filetype == EVD_FileType.BLOODBORNE
              or filetype == EVD_FileType.DARK_SOULS_3_TEST
              or filetype == EVD_FileType.DARK_SOULS_3):
            (linked_files_name_offset, ) = struct.unpack_from(
                "<Q", file_content, offset=master_offset)
            linked_files_list.append(linked_files_name_offset)
    return (filetype, event_list, strings, linked_files_list, should_dcx)
    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()
Ejemplo n.º 3
0
def emeld_file_content_to_string_list(file_content):
    should_dcx = False
    if dcx_handler.appears_dcx(file_content):
        tmp = dcx_handler.uncompress_dcx_content(file_content)
        should_dcx = True
        file_content = tmp
    master_offset = 0

    master_offset = consume_byte(file_content, master_offset, b'\x45', 1)
    master_offset = consume_byte(file_content, master_offset, b'\x4C', 1)
    master_offset = consume_byte(file_content, master_offset, b'\x44', 1)
    master_offset = consume_byte(file_content, master_offset, b'\x00', 1)

    ((version_part_1, version_part_2),
     master_offset) = extract_struct("<II", file_content, master_offset)

    if version_part_1 == 0x00000000 and version_part_2 == 0x00CC0065:
        filetype = EVD_FileType.DARK_SOULS_1
    elif version_part_1 == 0x0000FF00 and version_part_2 == 0x00CC0065:
        filetype = EVD_FileType.BLOODBORNE
    else:
        raise ValueError(("Unrecognized version bytes %08x %08x." +
                          "Could not determine ELD version.") %
                         (version_part_1, version_part_2))

    ((file_size, ), master_offset) = extract_struct("<I", file_content,
                                                    master_offset)

    if filetype == EVD_FileType.DARK_SOULS_1:
        ((event_count, event_offset),
         master_offset) = extract_struct("<II", file_content, master_offset)
        ((_, _),
         master_offset) = extract_struct("<II", file_content,
                                         master_offset)  # 0, strings_offset
        ((_, _),
         master_offset) = extract_struct("<II", file_content,
                                         master_offset)  # 0, strings_offset
        ((strings_length, strings_offset),
         master_offset) = extract_struct("<II", file_content, master_offset)
        ((_, _), master_offset) = extract_struct("<II", file_content,
                                                 master_offset)  # 0, 0
    else:
        ((event_count, event_offset),
         master_offset) = extract_struct("<QQ", file_content, master_offset)
        ((_, _),
         master_offset) = extract_struct("<QQ", file_content,
                                         master_offset)  # 0, strings_offset
        ((_, _),
         master_offset) = extract_struct("<QQ", file_content,
                                         master_offset)  # 0, strings_offset
        ((strings_length, strings_offset),
         master_offset) = extract_struct("<QQ", file_content, master_offset)

    master_offset = event_offset
    return_list = []
    for i in range(event_count):
        if filetype == EVD_FileType.DARK_SOULS_1:
            ((event_id, string_offset, _),
             master_offset) = extract_struct("<III", file_content,
                                             master_offset)
        else:
            ((event_id, string_offset),
             master_offset) = extract_struct("<QQ", file_content,
                                             master_offset)
        (value, _) = extract_utf16z(file_content,
                                    strings_offset + string_offset)
        return_list.append((event_id, value))
    return (return_list, filetype, should_dcx)