Example #1
0
T_Status        C_DDI_Rituals::Parse_Index_Cell(T_XML_Node node, T_Ritual_Info * info,
                                                T_Int32U subtype)
{
    T_Status        status;

    status = Get_Required_Child_PCDATA(&info->level, node, "Level");
    if (!x_Is_Success(status))
        x_Status_Return(status);
    status = Get_Required_Child_PCDATA(&info->price, node, "Price");
    if (!x_Is_Success(status))
        x_Status_Return(status);

    /* If our level is '0', that's obviously nonsense so get
        rid of it
    */
    if ((info->level[0] == '0') && (info->level[1] == '\0'))
        info->level = "";

    status = Get_Required_Child_PCDATA(&info->componentcost, node, "ComponentCost");
    if (!x_Is_Success(status))
        x_Status_Return(status);
    status = Get_Required_Child_PCDATA(&info->keyskill, node, "KeySkillDescription");
    if (!x_Is_Success(status))
        x_Status_Return(status);

    x_Status_Return_Success();
}
Example #2
0
static T_Status Append_Extension(T_Glyph_Ptr filename, T_File_Attribute attributes,
                             T_Void_Ptr context)
{
    long            result;
    bool            is_partial;
    T_Glyph_Ptr     base_filename, output_folder = (T_Glyph_Ptr) context;
    T_XML_Node      root;
    T_XML_Document  document;
    T_Filename      output_filename;

    if ((attributes & e_filetype_directory) != 0)
        x_Status_Return_Success();

    /* Read the file in from the temporary folder as an XML document
    */
    result = XML_Read_Document(&document, &l_data, filename);
    if (x_Trap_Opt(result != 0)) {
        Log_Message("Could not read XML document.");
        x_Status_Return_Success();
        }

    /* Get the base filename to use for this file
    */
    base_filename = strrchr(filename, DIR[0]);
    if (x_Trap_Opt(base_filename == NULL))
        x_Status_Return(LWD_ERROR);
    base_filename++;
    if (x_Trap_Opt(*base_filename == '\0'))
        x_Status_Return(LWD_ERROR);

    /* Query and unset the artificial "partial" flag we added, so it
        doesn't confuse things by showing up in the real data file we're
        about to output.
    */
    result = XML_Get_Document_Node(document, &root);
    if (x_Trap_Opt(result != 0))
        x_Status_Return_Success();
    is_partial = XML_Read_Boolean_Attribute(root, "ispartial");
    XML_Write_Boolean_Attribute(root, "ispartial", false);

    /* Append any extensions to the document
     */
    Append_Extensions(document, output_folder, base_filename, is_partial);

    /* Back up any existing file and write out the new one
    */
    sprintf(output_filename, "%s" DIR "%s", output_folder, base_filename);
    Backup_Existing(output_filename);
    result = XML_Write_Document(document, output_filename, false, true);
    if (x_Trap_Opt(result != 0)) {
        Log_Message("Could not write XML document.");
        x_Status_Return_Success();
        }

    XML_Destroy_Document(document);
    x_Status_Return_Success();
}
Example #3
0
static T_Status Login(T_WWW internet, T_Glyph_Ptr email, T_Glyph_Ptr password, bool is_report_success)
{
    static T_Glyph_Ptr      l_last_email = NULL;
    static T_Glyph_Ptr      l_last_password = NULL;
    T_Status                status;
    T_Glyph_Ptr             ptr, newline, invalid;

    /* Save our last email and password if given, or re-use them if we weren't
        passed in an email or password
    */
    if (email != NULL)
        l_last_email = email;
    else
        email = l_last_email;
    if (password != NULL)
        l_last_password = password;
    else
        password = l_last_password;

    /* If we don't have an email or password, we can't log in
    */
    if ((email[0] == '\0') || (password[0] == '\0'))
        x_Status_Return_Success();

    /* Set up our arrays of name/value parameters to use for the login POST
    */
    T_Glyph_Ptr             names[] = { "email", "password" };
    T_Glyph_Ptr             values[] = { email, password };

    /* Logging in is a simple POST of our username & password to the D&DI server
    */
    status = WWW_HTTP_Post(internet,LOGIN_URL,x_Array_Size(names),names,values,&ptr);
    if (x_Trap_Opt(!x_Is_Success(status))) {
        Log_Message("Couldn't make login HTTP post!\n", true);
        x_Status_Return(status);
        }

    /* If the string 'invalid' appears in the first line of our response,
        it's a failed login.
    */
    newline = strchr(ptr, '\n');
    if (newline == NULL) {
        Log_Message("\n**** Login error - could not log in to D&D insider.\n", true);
        x_Status_Return(LWD_ERROR);
        }
    invalid = strstr(ptr, "invalid");
    if ((invalid != NULL) && (invalid < newline)) {
        Log_Message("\n**** Login error - username / password not recognised.\n", true);
        x_Status_Return(LWD_ERROR);
        }

    Log_Message("Login succeeded.\n", is_report_success);
    l_is_password = true;
    x_Status_Return_Success();
}
Example #4
0
T_Status        C_DDI_Deities::Parse_Index_Cell(T_XML_Node node, T_Deity_Info * info,
                                                T_Int32U subtype)
{
    T_Status        status;

    status = Get_Required_Child_PCDATA(&info->alignment, node, "Alignment");
    if (!x_Is_Success(status))
        x_Status_Return(status);

    x_Status_Return_Success();
}
Example #5
0
static T_Status Create_Document(T_XML_Document * document, T_XML_Node * root, T_Glyph_Ptr name,
                                T_XML_Element * element)
{
    T_Int32S                result;
    T_Glyph                 buffer[500];

    result = XML_Create_Document(document, element, false);
    if (x_Trap_Opt(result != 0)) {
        sprintf(buffer, "Could not create %s XML document.", name);
        Log_Message(buffer);
        x_Status_Return(LWD_ERROR);
        }

    result = XML_Get_Document_Node(*document, root);
    if (x_Trap_Opt(result != 0)) {
        sprintf(buffer, "Could not get %s XML document root.", name);
        Log_Message(buffer);
        x_Status_Return(LWD_ERROR);
        }

    x_Status_Return_Success();
}
Example #6
0
T_Status    C_DDI_Skills::Output_Entry(T_XML_Node root, T_Skill_Info * info)
{
    T_Status        status = LWD_ERROR;
    T_Int32S        result;
    T_Glyph         skill_id[UNIQUE_ID_LENGTH+1], buffer[500];

    /* Generate a unique id for the class - if we can't, get out
    */
    if (!Generate_Thing_Id(skill_id, "sk", info->name, &m_id_list)) {
        sprintf(buffer, "Error generating unique id for skill %s\n", info->name);
        Log_Message(buffer);
        goto cleanup_exit;
        }
    info->id = m_pool->Acquire(skill_id);

    /* Now that all our required information has been discerned, we can go
        ahead and create the node
    */
    result = Create_Thing_Node(root, m_term, "Skill", info->name, skill_id, info->description,
                                UNIQUENESS_UNIQUE, info->source, &info->node, info->is_partial);
    if (x_Trap_Opt(result != 0))
        goto cleanup_exit;

    /* Add this skill to our internal list of skills
    */
    Mapping_Add("skill", info->name, m_pool->Acquire(skill_id));

    /* If we don't have a password, we can't get any more data
    */
    if (info->is_partial) {
        status = SUCCESS;
        goto cleanup_exit;
        }

    /* Add a link for this skill's linked attribute
    */
    Output_Link(info->node, "attribute", info->attribute, info, "attr");

    status = SUCCESS;

cleanup_exit:
    x_Status_Return(status);
}
Example #7
0
static T_Status Finish_Document(T_XML_Document document, T_Glyph_Ptr output_folder,
                                T_Glyph_Ptr filename)
{
    T_Int32S                result;
    T_Glyph                 buffer[MAX_FILE_NAME+1];

    sprintf(buffer, "%s" DIR "%s", output_folder, filename);
    Backup_Existing(buffer);

    /* Output the document into our output folder
    */
    result = XML_Write_Document(document, buffer, false, true);
    if (x_Trap_Opt(result != 0)) {
        sprintf(buffer, "Could not write %s XML document.", filename);
        Log_Message(buffer);
        x_Status_Return(LWD_ERROR);
        }

    x_Status_Return_Success();
}
Example #8
0
T_Status    C_DDI_Backgrounds::Output_Entry(T_XML_Node root, T_Background_Info * info)
{
    T_Status        status = LWD_ERROR;
    T_Int32S        result;
    T_Glyph         background_id[UNIQUE_ID_LENGTH+1], buffer[10000];

    /* Generate a unique id for the background - if we can't, get out
    */
    if (!Generate_Thing_Id(background_id, "bg", info->name, &m_id_list)) {
        sprintf(buffer, "Error generating unique id for background %s\n", info->name);
        Log_Message(buffer);
        goto cleanup_exit;
        }
    info->id = m_pool->Acquire(background_id);

    /* Now that all our required information has been discerned, we can go
        ahead and create the node
    */
    result = Create_Thing_Node(root, m_term, "Background", info->name, background_id, info->description,
                                UNIQUENESS_UNIQUE, info->source, &info->node, info->is_partial);
    if (x_Trap_Opt(result != 0))
        goto cleanup_exit;

    if ((info->type != NULL) && (info->type[0] != '\0'))
        Output_Tag(info->node, "BackType", info->type, info, "backtype", NULL, true, true);
    if ((info->campaign != NULL) && (info->campaign[0] != '\0'))
        Output_Tag(info->node, "BackCamp", info->campaign, info, "backcamp", NULL, true, true);

    Output_Options(info, true, &m_id_list, m_pool);
    Output_Options(info, false, &m_id_list, m_pool);

    /* Add this path to our internal list of backgrounds
    */
    Mapping_Add("background", info->name, m_pool->Acquire(background_id));

    status = SUCCESS;

cleanup_exit:
    x_Status_Return(status);
}
Example #9
0
/* Note - this function should be resilient to stuff in the HTML changing, so
    if we don't find what we expect, we should just return successfully and
    not complain about it.
*/
T_Status        C_DDI_Deities::Parse_Entry_Details(T_Glyph_Ptr contents, T_Deity_Info * info,
                                                    T_Glyph_Ptr ptr, T_Glyph_Ptr checkpoint,
                                                    vector<T_Deity_Info> * extras)
{
    T_Status            status;
    T_Glyph_Ptr         end;

    ptr = Extract_Text(&info->flavor, m_pool, ptr, checkpoint, "<b>Type: </b>", "<br");

    ptr = Extract_Text(&info->alignment, m_pool, ptr, checkpoint, "<b>Alignment :</b>", "</b><br");

    ptr = Extract_Text(&info->gender, m_pool, ptr, checkpoint, "<b>Gender:</b>", "<br");

    ptr = Extract_Text(&info->sphere, m_pool, ptr, checkpoint, "<b>Sphere:</b>", "<br");

    ptr = Extract_Text(&info->dominion, m_pool, ptr, checkpoint, "<b>Dominion:</b>", "<br");

    ptr = Extract_Text(&info->priests, m_pool, ptr, checkpoint, "<b>Priests:</b>", "<br");

    ptr = Extract_Text(&info->adjective, m_pool, ptr, checkpoint, "<b>Adjective:</b>", "<br");

    /* The description text appears after the end of the paragraph node
    */
    ptr = Find_Text(ptr, "</p>", checkpoint, true, false);
    if (x_Trap_Opt(ptr == NULL))
        x_Status_Return_Success();
    end = Find_Text(ptr, "<p>", checkpoint, false, false);
    if (x_Trap_Opt(end == NULL))
        x_Status_Return_Success();
    *end = '\0';
    status = Parse_Description_Text(&info->description, ptr, m_pool);
    if (x_Trap_Opt(!x_Is_Success(status)))
        x_Status_Return(status);
    *end = '<';

    x_Status_Return_Success();
}
Example #10
0
T_Status    C_DDI_Rituals::Output_Entry(T_XML_Node root, T_Ritual_Info * info)
{
    T_Status        status = LWD_ERROR;
    T_Int32S        result;
    T_Glyph_Ptr     ptr, src;
    bool            is_level;
    T_Glyph         ritual_id[UNIQUE_ID_LENGTH+1], buffer[5000];

    /* Generate a unique id for the ritual - if we can't, get out
    */
    if (!Generate_Thing_Id(ritual_id, "rt", info->name, &m_id_list)) {
        sprintf(buffer, "Error generating unique id for ritual %s\n", info->name);
        Log_Message(buffer);
        goto cleanup_exit;
        }
    info->id = m_pool->Acquire(ritual_id);

    /* Now that all our required information has been discerned, we can go
        ahead and create the node
    */
    result = Create_Thing_Node(root, m_term, "Ritual", info->name, ritual_id, info->description,
                                UNIQUENESS_NONE, info->source, &info->node, info->is_partial);
    if (x_Trap_Opt(result != 0))
        goto cleanup_exit;

    is_level = false;
    if (!x_Is_Digit(info->level[0]))
        ptr = info->level;
    else {
        is_level = true;
        Output_Tag(info->node, "ReqLevel", As_Int(atoi(info->level)), info);
        ptr = strchr(info->level, ' ');
        while ((ptr != NULL) && (*ptr == ' '))
            ptr++;
        }
    if ((ptr != NULL) && (ptr[0] != '\0'))
        Output_Field(info->node, "ritLevText", ptr, info);
    else if (!is_level) {
        sprintf(buffer, "No ritual level found for ritual %s\n", info->name);
        Log_Message(buffer);
        }

    Output_Field(info->node, "ritCompCst", info->componentcost, info);

    Output_Key_Skills(info);

    /* If we don't have a password, we can't get any more data
    */
    if (info->is_partial) {
        status = SUCCESS;
        goto cleanup_exit;
        }

    Split_To_Dynamic_Tags(info->node, info, info->category, ",", "RitualCat", "ritualcat");

    Output_Field(info->node, "ritTime", info->time, info);

    if ((info->duration != NULL) && (info->duration[0] != '\0'))
        Output_Field(info->node, "ritLasts", info->duration, info);

    if (x_Is_Digit(info->price[0])) {
        strcpy(buffer, info->price);
        ptr = buffer;
        src = buffer;
        while (*src != '\0') {
            if (*src != ',')
                *ptr++ = *src;
            src++;
            }
        *ptr = '\0';
        Output_Field(info->node, "ritPrice", As_Int(atoi(buffer)), info);
        }
    else if ((strcmp(info->price, "Unique") != 0) && (strcmp(info->price, "-") != 0) &&
             (strcmp(info->price, "None") != 0)) {
        sprintf(buffer, "Invalid price '%s' found for ritual %s\n", info->price, info->name);
        Log_Message(buffer);
        goto cleanup_exit;
        }

    status = SUCCESS;

cleanup_exit:
    x_Status_Return(status);
}
Example #11
0
/* Note - this function should be resilient to stuff in the HTML changing, so
    if we don't find what we expect, we should just return successfully and
    not complain about it.
*/
T_Status        C_DDI_Rituals::Parse_Entry_Details(T_Glyph_Ptr contents, T_Ritual_Info * info,
                                                    T_Glyph_Ptr ptr, T_Glyph_Ptr checkpoint,
                                                    vector<T_Ritual_Info> * extras)
{
    T_Status            status;
    T_Glyph_Ptr         end;
    T_Glyph_Ptr         starts[] = { "???", };
    T_Glyph_Ptr         ends[] = { "<br", "</p", "</span" };

    ptr = Extract_Text(&info->flavor, m_pool, ptr, checkpoint, "<span class=\"flavor\">", "</span>");
    Strip_Bad_Characters(info->flavor);

    /* This was parsed from the index above, but we might as well grab it again
        here, in case there's more information
    */
    starts[0] = "<b>Component Cost</b>:";
    ptr = Extract_Text(&info->componentcost, m_pool, ptr, checkpoint, starts, x_Array_Size(starts),
                        ends, x_Array_Size(ends));
    Strip_Bad_Characters(info->componentcost);

    /* Although we've parsed the skill out of the index, replace it if we can -
        the one on the actual page has more data (e.g. "no check" info).
    */
    starts[0] = "<b>Key Skill</b>:";
    ptr = Extract_Text(&info->keyskill, m_pool, ptr, checkpoint, starts, x_Array_Size(starts),
                        ends, x_Array_Size(ends));
    Strip_Bad_Characters(info->keyskill);

    /* Although we've parsed the level out of the index, replace it if we can -
        the one on the actual page has more data.
    */
    starts[0] = "<b>Level</b>:";
    ptr = Extract_Text(&info->level, m_pool, ptr, checkpoint, starts, x_Array_Size(starts),
                        ends, x_Array_Size(ends));
    Strip_Bad_Characters(info->level);

    starts[0] = "<b>Category</b>:";
    ptr = Extract_Text(&info->category, m_pool, ptr, checkpoint, starts, x_Array_Size(starts),
                        ends, x_Array_Size(ends));
    Strip_Bad_Characters(info->category);

    starts[0] = "<b>Time</b>:";
    ptr = Extract_Text(&info->time, m_pool, ptr, checkpoint, starts, x_Array_Size(starts),
                        ends, x_Array_Size(ends));
    Strip_Bad_Characters(info->time);

    starts[0] = "<b>Duration</b>:";
    ptr = Extract_Text(&info->duration, m_pool, ptr, checkpoint, starts, x_Array_Size(starts),
                        ends, x_Array_Size(ends));
    Strip_Bad_Characters(info->duration);

    starts[0] = "<b>Prerequisite</b>:";
    ptr = Extract_Text(&info->prerequisite, m_pool, ptr, checkpoint, starts, x_Array_Size(starts),
                        ends, x_Array_Size(ends));
    Strip_Bad_Characters(info->prerequisite);

    /* Parse out any paragraph close tag we encounter here
    */
    if (strnicmp(ptr, "</p>", 4) == 0)
        ptr += 4;

    /* Now parse the rest of the description - it might end in a </p> tag, or
        it might not! Who knows!
    */
    end = Find_Text(ptr, "</p>", checkpoint, false, false);
    if (end == NULL)
        end = checkpoint;
    *end = '\0';
    status = Parse_Description_Text(&info->description, ptr, m_pool);
    if (x_Trap_Opt(!x_Is_Success(status)))
        x_Status_Return(status);
    *end = '<';

    x_Status_Return_Success();
}
Example #12
0
T_Status    C_DDI_Monsters::Output_Entry(T_XML_Node root, T_Monster_Info * info)
{
    x_Status_Return(LWD_ERROR_NOT_IMPLEMENTED);
}
Example #13
0
static T_Status Restore_Old_File(T_Glyph_Ptr filename, T_File_Attribute attributes,
                             T_Void_Ptr context)
{
    T_Status            status;
    T_Glyph_Ptr         base_filename, output_folder = (T_Glyph_Ptr) context;
    T_Filename          file_old, file_new, file_temp;
    T_Glyph             buffer[1000];

    if ((attributes & e_filetype_directory) != 0)
        x_Status_Return_Success();

    /* Get the base filename to use for this file
    */
    base_filename = strrchr(filename, DIR[0]);
    if (x_Trap_Opt(base_filename == NULL))
        x_Status_Return(LWD_ERROR);
    base_filename++;
    if (x_Trap_Opt(*base_filename == '\0'))
        x_Status_Return(LWD_ERROR);

    /* Get the temporary filename we'll use for stuff
    */
    sprintf(file_temp, "%s" DIR "temporary.ignore", output_folder);

    /* Get filenames for our source, and target files. The source is the
        one we just found; the target is that with ".saved" chopped off the
        end.
    */
    sprintf(file_old, "%s" DIR "%s", output_folder, base_filename);
    strcpy(file_new, file_old);
    file_new[strlen(file_new) - strlen(BACKUP_EXTENSION)] = '\0';
    sprintf(buffer, "Restoring %s...\n", base_filename);
    Log_Message(buffer, true);

    /* We want to swap the two files, so that the new is replaced by the
        old.
    */
    FileSys_Delete_File(file_temp, FALSE);
    if (FileSys_Does_File_Exist(file_new)) {
        status = FileSys_Copy_File(file_temp, file_new, FALSE);
        if (!x_Is_Success(status)) {
            sprintf(buffer, "Couldn't make a backup of %s!\n", file_new);
            Log_Message(buffer, true);
            }
        }
    if (FileSys_Does_File_Exist(file_old)) {
        status = FileSys_Copy_File(file_new, file_old, FALSE);
        if (!x_Is_Success(status)) {
            sprintf(buffer, "Couldn't restore old file %s!\n", file_old);
            Log_Message(buffer, true);
            }
        }
    if (FileSys_Does_File_Exist(file_temp)) {
        status = FileSys_Copy_File(file_old, file_temp, FALSE);
        if (!x_Is_Success(status)) {
            sprintf(buffer, "Couldn't restore backup of new file %s!\n", file_new);
            Log_Message(buffer, true);
            }
        }
    FileSys_Delete_File(file_temp, FALSE);
    x_Status_Return_Success();
}
Example #14
0
static T_Status Crawl_Data(bool use_cache, T_Glyph_Ptr email, T_Glyph_Ptr password,
                            T_Filename output_folder, bool is_clear)
{
    T_Status                status;
    T_WWW                   internet = NULL;
    bool                    is_exists;
    T_Glyph_Ptr             path_ptr;
    T_Filename              folder;
    T_XML_Document          doc_languages = NULL;
    T_XML_Document          doc_wepprops = NULL;
    T_XML_Document          doc_sources = NULL;
    C_Pool                  pool(10000000,10000000);
    C_DDI_Deities           deities(&pool);
    C_DDI_Skills            skills(&pool);
    C_DDI_Rituals           rituals(&pool);
    C_DDI_Items             items(&pool);
    C_DDI_Classes           classes(&pool);
    C_DDI_Races             races(&pool);
    C_DDI_Paragons          paragons(&pool);
    C_DDI_Epics             epics(&pool);
    C_DDI_Feats             feats(&pool);
    C_DDI_Powers            powers(&pool);
    C_DDI_Monsters          monsters(&pool);
    C_DDI_Backgrounds       backgrounds(&pool);
    vector<C_DDI_Common *>  list;
    vector<C_DDI_Single_Common *>   singles;

    /* Add all of our downloader classes to the list
    */
    list.push_back(&deities);
    list.push_back(&skills);
    list.push_back(&rituals);
    list.push_back(&items);
    list.push_back(&classes);
    list.push_back(&races);
    list.push_back(&paragons);
    list.push_back(&epics);
    list.push_back(&feats);
    list.push_back(&powers);
//Monsters aren't supported yet...
#ifdef NOTYET
    list.push_back(&monsters);
#endif
    list.push_back(&backgrounds);

    /* Find a temporary folder to use
    */
    Get_Temporary_Folder(folder);
    path_ptr = folder + strlen(folder);

    /* Search for the folder and create it if it doesn't exist
    */
    is_exists = FileSys_Does_Folder_Exist(folder);
    if (!is_exists) {
        status = FileSys_Create_Directory(folder);
        if (x_Trap_Opt(!x_Is_Success(status))) {
            Log_Message("Couldn't create temporary folder.");
            x_Status_Return(LWD_ERROR);
            }
        }

    /* If we're not using cached copies, log in to D&D insider (if we don't
        have a username and password, this just establishes the connection)
    */
    if (!use_cache) {
        Log_Message("Connecting to server...\n", true);

        /* Open a connection to the server first
        */
        status = WWW_HTTP_Open(&internet,LOGIN_URL,NULL,NULL,NULL);
        if (x_Trap_Opt(!x_Is_Success(status))) {
            Log_Message("Couldn't open connection to login server!\n", true);
            goto cleanup_exit;
            }

        status = Login(internet, email, password, true);
        if (x_Trap_Opt(!x_Is_Success(status)))
            goto cleanup_exit;
        }

    /* If we're not using the cache, we want to make sure we have fresh copies
        of everything, so delete the files in our temporary folder
    */
    if (!use_cache) {
        strcpy(path_ptr,"*.*");
        FileSys_Delete_Files(folder);
        *path_ptr = '\0';
        }

    /* Create XML documents for deities languages - the crawlers can add their
        own entries for these during processing
    */
    status = Create_Document(&doc_languages, &l_language_root, "language",
                                DTD_Get_Data());
    if (!x_Is_Success(status))
        goto cleanup_exit;
    status = Create_Document(&doc_wepprops, &l_wepprop_root, "weapon property",
                                DTD_Get_Augmentation());
    if (!x_Is_Success(status))
        goto cleanup_exit;
    status = Create_Document(&doc_sources, &l_source_root, "source",
                                DTD_Get_Augmentation());
    if (!x_Is_Success(status))
        goto cleanup_exit;

    /* First, download all the index pages if we need to
    */
    if (!use_cache) {
        for (ddi_iter it = list.begin(); it != list.end(); ++it) {
            status = (*it)->Download_Index(folder, internet);
            if (x_Trap_Opt(!x_Is_Success(status)))
                goto cleanup_exit;
            }
        for (single_iter it = singles.begin(); it != singles.end(); ++it) {
            status = (*it)->Download(folder, internet);
            if (x_Trap_Opt(!x_Is_Success(status)))
                goto cleanup_exit;
            }
        }

    /* Read them in
    */
    for (ddi_iter it = list.begin(); it != list.end(); ++it) {
        status = (*it)->Read_Index(folder);
        if (x_Trap_Opt(!x_Is_Success(status)))
            goto cleanup_exit;
        }
    for (single_iter it = singles.begin(); it != singles.end(); ++it) {
        status = (*it)->Read(folder);
        if (x_Trap_Opt(!x_Is_Success(status)))
            goto cleanup_exit;
        }

    /* Now download the rest of the content all at once
        NOTE: As of March 2011, there's no further content for download unless
            you have a password.
    */
    if (!use_cache && l_is_password) {
        for (ddi_iter it = list.begin(); it != list.end(); ++it) {
            status = (*it)->Download_Content(folder, internet);
            if (x_Trap_Opt(!x_Is_Success(status)))
                goto cleanup_exit;
            }

        /* It's possible that some downloads were screwed up due to the
            servers acting weirdly. If so, they were added to a failed list,
            so try them again
        */
        Retry_Failed_Downloads(internet);
        }

    /* Read it in and process it
    */
    for (ddi_iter it = list.begin(); it != list.end(); ++it) {
        status = (*it)->Read_Content(folder);
        if (x_Trap_Opt(!x_Is_Success(status)))
            goto cleanup_exit;
        }

    /* Process all our stuff
    */
    for (ddi_iter it = list.begin(); it != list.end(); ++it) {
        status = (*it)->Process();
        if (x_Trap_Opt(!x_Is_Success(status)))
            goto cleanup_exit;
        }
    for (single_iter it = singles.begin(); it != singles.end(); ++it) {
        status = (*it)->Process();
        if (x_Trap_Opt(!x_Is_Success(status)))
            goto cleanup_exit;
        }

    /* Post-process all our stuff - this requires it to have been output first,
        and puts finishing touches on everything
    */
    for (ddi_iter it = list.begin(); it != list.end(); ++it) {
        status = (*it)->Post_Process(folder);
        if (x_Trap_Opt(!x_Is_Success(status)))
            goto cleanup_exit;
        }
    for (single_iter it = singles.begin(); it != singles.end(); ++it) {
        status = (*it)->Post_Process(folder);
        if (x_Trap_Opt(!x_Is_Success(status)))
            goto cleanup_exit;
        }

    /* Output our languages to the temporary folder, so we can append
        extensions to them later
    */
    status = Finish_Document(doc_languages, folder, LANGUAGE_FILENAME);
    if (!x_Is_Success(status))
        goto cleanup_exit;

    /* Now append any fixup extensions that are needed to compensate for the
        data being terrible
    */
    Append_Extensions(folder, output_folder);

    /* Load the old "powers" XML document and try and copy any wizard powers
        out of it
    */
//We don't need to do this any more, but keep the code around just in case...
//    Fixup_Wizard_Powers(output_folder);

    /* Sources and weapon properties don't need any extensions, so they can go
        directly into the output folder - plus they're augmentation files,
        which aren't messed with anyway
    */
    status = Finish_Document(doc_sources, output_folder, SOURCE_FILENAME);
    if (!x_Is_Success(status))
        goto cleanup_exit;
    status = Finish_Document(doc_wepprops, output_folder, WEPPROP_FILENAME);
    if (!x_Is_Success(status))
        goto cleanup_exit;

    /* wrapup everything
    */
cleanup_exit:
    l_language_root = NULL;
    if (doc_languages != NULL)
        XML_Destroy_Document(doc_languages);
    l_wepprop_root = NULL;
    if (doc_wepprops != NULL)
        XML_Destroy_Document(doc_wepprops);
    l_source_root = NULL;
    if (doc_sources != NULL)
        XML_Destroy_Document(doc_sources);
    if (internet != NULL)
        WWW_Close_Server(internet);

    /* Delete everything in our folder if required
    */
    if (is_clear) {
        strcpy(path_ptr,"*.*");
        FileSys_Delete_Files(folder);
        *path_ptr = '\0';
        }

    x_Status_Return(status);
}