private void RefreshAssets(IProgressDialog dialog)
        {
            Dictionary<string, string> assetFiles = new Dictionary<string, string>();
            List<string> outdatedNames = new List<string>();
            CacheResult result;

            // clear the assets
            m_assets = null;

            // make sure our character list is up to date
            dialog.Update("Refreshing character list...");
            Program.RefreshCharacters();
            dialog.Update(1, 3 + Program.Characters.Rows.Count);

            // fetch the asset XML
            dialog.Update("Querying API for asset lists...");
            foreach (DataRow row in Program.Characters.Rows)
            {
                int userID = Convert.ToInt32(row["userID"]);
                int characterID = Convert.ToInt32(row["characterID"]);
                int corporationID = Convert.ToInt32(row["corporationID"]);
                string apiKey = Program.ApiKeys.Rows.Find(userID)["apiKey"].ToString();
                string characterName = row["name"].ToString();
                string corporationName = row["corporationName"].ToString();
                bool queryCorp = Convert.ToBoolean(row["queryCorp"]);

                // fetch character assets
                if (!assetFiles.ContainsKey(characterName))
                {
                    result = EveApiHelper.GetCharacterAssetList(userID, apiKey, characterID);
                    switch (result.State)
                    {
                        case CacheState.Cached:
                            assetFiles[characterName] = result.Path;
                            break;
                        case CacheState.CachedOutOfDate:
                            assetFiles[characterName] = result.Path;
                            outdatedNames.Add(characterName);
                            break;
                        default:
                            throw new ApplicationException("Failed to retrieve asset data for " + characterName, result.Exception);
                    }
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("Odd, got two records for the same character name... " + characterName);
                }

                // fetch corporation assets?
                if (queryCorp && !assetFiles.ContainsKey(corporationName))
                {
                    // attempt the query
                    result = EveApiHelper.GetCorporationAssetList(userID, apiKey, characterID, corporationID);

                    // check whether we got an eve error about not being a director
                    if (result.Exception != null && result.Exception is EveApiException && ((EveApiException)result.Exception).ErrorCode == 209)
                    {
                        System.Diagnostics.Debug.WriteLine(characterName + " is not a Director or CEO of " + corporationName + ".");
                        row["queryCorp"] = false;
                    }
                    else
                    {
                        switch (result.State)
                        {
                            case CacheState.Cached:
                                assetFiles[corporationName] = result.Path;
                                break;
                            case CacheState.CachedOutOfDate:
                                assetFiles[corporationName] = result.Path;
                                outdatedNames.Add(corporationName);
                                break;
                            default:
                                throw new ApplicationException("Failed to retrieve asset data for " + corporationName, result.Exception);
                        }
                    }
                }

                // progress
                dialog.Advance();
            }

            // inform the user about any files that could not be refreshed
            if (outdatedNames.Count > 0)
            {
                StringBuilder message = new StringBuilder();

                // prepare the semi-friendly message
                message.Append("An error occurred while refreshing assets for the characters and/or\ncorporations listed below. Cached data will be used instead. Your assets might\nbe out of date.\n");
                foreach (string name in outdatedNames)
                {
                    message.Append("\n");
                    message.Append(name);
                }

                // prepare the code to be invoked
                MethodInvoker code = delegate()
                {
                    MessageBox.Show(this, message.ToString(), "Using Cached Assets", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                };

                // invoke it
                if (this.InvokeRequired)
                    this.Invoke(code);
                else
                    code();
            }

            // init the database
            dialog.Update("Initializing local asset database...");
            AssetCache.InitializeDB(true);
            dialog.Advance();

            // parse the files
            dialog.Update("Parsing asset XML...");
            foreach (string characterName in assetFiles.Keys)
            {
                string assetFile = assetFiles[characterName];
                AssetCache.ParseAssets(assetFile, characterName);
            }
            dialog.Advance();
        }
        public static void RefreshCharacters(IProgressDialog dialog)
        {
            string path;
            string apiKey;
            int userID;
            DataTable tempChars;
            CacheResult result;

            // this is where we're gonna put the characters while we query and read XML and stuff
            tempChars = m_characters.Clone();

            // progress
            if (dialog != null)
                dialog.Update("Refreshing character lists from API...", 0, Program.ApiKeys.Rows.Count);
            
            foreach (DataRow row in Program.ApiKeys.Rows)
            {
                // grab the account ID and key from the row
                userID = Convert.ToInt32(row["userID"]);
                apiKey = row["apiKey"].ToString();

                // query the API
                result = EveApiHelper.GetCharacters(userID, apiKey);
                if (result.State == CacheState.Uncached)
                {
                    MessageBox.Show("Failed to fetch characters for UserID " + userID.ToString() + result.Exception == null ? "" : "\n\n" + result.Exception.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    continue;
                }
                else
                {
                    path = result.Path;
                }

                // parse the XML
                using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
                {
                    XPathDocument doc = new XPathDocument(fs);
                    XPathNavigator nav = doc.CreateNavigator();
                    XPathNodeIterator iter;
                    DataRow charRow, existingRow;

                    iter = nav.Select("/eveapi/result/rowset/row");

                    while (iter.MoveNext())
                    {
                        // create the new row
                        charRow = tempChars.NewRow();
                        charRow["userID"] = userID;
                        charRow["name"] = iter.Current.SelectSingleNode("@name").Value;
                        charRow["characterID"] = iter.Current.SelectSingleNode("@characterID").ValueAsInt;
                        charRow["corporationName"] = iter.Current.SelectSingleNode("@corporationName").Value;
                        charRow["corporationID"] = iter.Current.SelectSingleNode("@corporationID").ValueAsInt;
                        charRow["queryCorp"] = true;

                        // try to find a matching row from the current characters table and keep that queryCorp value if we do
                        existingRow = m_characters.Rows.Find(charRow["characterID"]);
                        if (existingRow != null)
                            charRow["queryCorp"] = existingRow["queryCorp"];

                        // add the row to the temp table
                        tempChars.Rows.Add(charRow);
                    }
                }

                // progress
                if (dialog != null)
                    dialog.Advance();
            }

            // clear our character list and replace it
            m_characters.Rows.Clear();
            foreach (DataRow row in tempChars.Rows)
                m_characters.LoadDataRow(row.ItemArray, true);
        }