public static void WriteBar(JournalLoadingPercentChangedEventArgs args) { StringBuilder output = new StringBuilder(); int fillLen = 0; char filler = '#', spacer = ' '; output.Append(" "); for (int i = 0; i < 10; i++) { char c = i < args.Label.Length ? args.Label[i] : ' '; output.Append(c); } output.Append(" ["); fillLen = Convert.ToInt32(((decimal)args.Percent / 100) * 60); for (int i = 0; i < 60; i++) { output.Append(i <= fillLen ? filler : spacer); } output.Append("] "); output.Append(args.Percent + "%"); lock (__consoleWriteLock) { Console.Write("\r"); Console.ForegroundColor = ConsoleColor.Cyan; Console.Write(output.ToString()); Console.ResetColor(); } }
public void CleanJournal(PurgeOptions options) { long oldPercent = 0; IEnumerable <string> userList = TShock.Users.GetUsers().Select(i => i.Name); List <long> deleteList = new List <long>(); JournalLoadingPercentChangedEventArgs args = new JournalLoadingPercentChangedEventArgs() { Label = "Purge", Percent = 0 }; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, args); } for (int i = 0; i < this.BankAccounts.Count; i++) { double pcc = (double)i / (double)BankAccounts.Count * 100; IBankAccount account = bankAccounts.ElementAtOrDefault(i); if ((options & PurgeOptions.RemoveOrphanedAccounts) == PurgeOptions.RemoveOrphanedAccounts && userList.Contains(account.UserAccountName) == false) { if (deleteList.Contains(account.BankAccountK) == false) { deleteList.Add(account.BankAccountK); continue; } } if ((options & PurgeOptions.RemoveZeroBalanceAccounts) == PurgeOptions.RemoveZeroBalanceAccounts && (account.Balance <= 0 && account.IsSystemAccount == false)) { if (deleteList.Contains(account.BankAccountK) == false) { deleteList.Add(account.BankAccountK); continue; } } if (oldPercent != (int)pcc) { args.Percent = (int)pcc; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, args); } oldPercent = (int)pcc; } } if (deleteList.Count > 0) { args.Label = "Clean"; args.Percent = 0; for (int i = 0; i < deleteList.Count; i++) { double pcc = (double)i / (double)deleteList.Count * 100; DeleteBankAccount(deleteList[i]); if (oldPercent != (int)pcc) { args.Percent = (int)pcc; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, args); } oldPercent = (int)pcc; } } } }
public bool LoadJournal() { MemoryStream uncompressedData; JournalLoadingPercentChangedEventArgs parsingArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Loading" }; JournalLoadingPercentChangedEventArgs finalArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Verify" }; ConsoleEx.WriteLineColour(ConsoleColor.Cyan, " Using XML journal - {0}", path); try { byte[] fileData = new byte[0]; initPoint: try { fileData = File.ReadAllBytes(path); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.DarkCyan; if (ex is System.IO.FileNotFoundException || ex is System.IO.DirectoryNotFoundException) { TShock.Log.ConsoleInfo(" * It appears you do not have a journal yet, one will be created for you."); SaveJournal(); //yes there are valid uses for goto, don't judge me fool goto initPoint; } else if (ex is System.Security.SecurityException) { TShock.Log.ConsoleError(" * Access denied to the journal file. Check permissions."); } else { TShock.Log.ConsoleError(" * Loading your journal failed: " + ex.Message); } } try { uncompressedData = GZipDecompress(fileData); } catch { TShock.Log.ConsoleError(" * Decompression failed."); return(false); } if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } try { Hashtable bankAccountMap = new Hashtable(); Hashtable transactionMap = new Hashtable(); //You can't use XDocument.Parse when you have an XDeclaration for some even dumber reason than the write //issue, XDocument has to be constructed from a .net 3.5 XmlReader in this case //or you get a parse exception complaining about literal content. using (XmlTextReader xmlStream = new XmlTextReader(uncompressedData)) { XDocument doc = XDocument.Load(xmlStream); var bankAccountList = doc.XPathSelectElements("/Journal/BankAccounts/BankAccount"); int bankAccountCount = bankAccountList.Count(); int i = 0; int oldPercent = 0; foreach (XElement elem in bankAccountList) { long bankAccountK = 0L; double percentComplete = (double)i / (double)bankAccountCount * 100; var account = new XmlBankAccount(this) { Description = elem.Attribute("Description").Value, UserAccountName = elem.Attribute("UserAccountName").Value, WorldID = long.Parse(elem.Attribute("WorldID").Value), Flags = (BankAccountFlags)Enum.Parse(typeof(BankAccountFlags), elem.Attribute("Flags").Value) }; if (!long.TryParse(elem.Attribute("BankAccountK").Value, out bankAccountK)) { //we've overwritten the old bank account key, add it to the old/new map bankAccountK = Interlocked.Increment(ref _bankAccountSeed); bankAccountMap.Add(elem.Attribute("BankAccountK").Value, bankAccountK); } account.BankAccountK = bankAccountK; //parse transactions under this node if (elem.Element("Transactions") != null) { foreach (XElement txElement in elem.Element("Transactions").Elements("Transaction")) { long transactionK = 0L; long transactionFK = 0; long amount = long.Parse(txElement.Attribute("Amount").Value); DateTime transactionDate; BankAccountTransactionFlags flags; long.TryParse(txElement.Attribute("BankAccountTransactionK").Value, out transactionK); long.TryParse(txElement.Attribute("BankAccountTransactionFK").Value, out transactionFK); DateTime.TryParse(txElement.Attribute("TransactionDateUtc").Value, out transactionDate); Enum.TryParse <BankAccountTransactionFlags>(txElement.Attribute("Flags").Value, out flags); //ignore orphaned transactions var trans = new XmlTransaction(account) { Amount = amount, BankAccountTransactionK = transactionK, BankAccountTransactionFK = transactionFK, TransactionDateUtc = transactionDate, Flags = flags }; trans.Message = txElement.Attribute("Message") != null?txElement.Attribute("Message").Value : null; account.AddTransaction(trans); } } account.SyncBalance(); if (oldPercent != (int)percentComplete) { parsingArgs.Percent = (int)percentComplete; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } oldPercent = (int)percentComplete; } Interlocked.Increment(ref i); bankAccounts.Add(account); } Interlocked.Exchange(ref _bankAccountSeed, bankAccounts.Count() > 0 ? bankAccounts.Max(sum => sum.BankAccountK) : 0); //delete transactions with duplicate IDs var qAccounts = from summary in bankAccounts group summary by summary.BankAccountK into g where g.Count() > 1 select new { name = g.Key, count = g.Count() }; long[] duplicateAccounts = qAccounts.Select(pred => pred.name).ToArray(); int removedAccounts = bankAccounts.RemoveAll(pred => duplicateAccounts.Contains(pred.BankAccountK)); if (removedAccounts > 0) { TShock.Log.Warn("seconomy journal: removed " + removedAccounts + " accounts with duplicate IDs."); } //transactions in the old schema. int tranCount = doc.XPathSelectElements("/Journal/Transactions/Transaction").Count(); i = 0; //reset index foreach (XElement elem in doc.XPathSelectElements("/Journal/Transactions/Transaction")) { //Parallel.ForEach(doc.XPathSelectElements("/Journal/Transactions/Transaction"), (elem) => { double percentComplete = (double)i / (double)tranCount * 100; long bankAccountFK = 0L; long transactionK = 0L; long amount = long.Parse(elem.Attribute("Amount").Value); if (!long.TryParse(elem.Attribute("BankAccountFK").Value, out bankAccountFK)) { if (bankAccountMap.ContainsKey(elem.Attribute("BankAccountFK").Value)) { Interlocked.Exchange(ref bankAccountFK, (long)bankAccountMap[elem.Attribute("BankAccountFK").Value]); } } IBankAccount bankAccount = GetBankAccount(bankAccountFK); long.TryParse(elem.Attribute("BankAccountTransactionK").Value, out transactionK); //ignore orphaned transactions if (bankAccount != null) { var trans = new XmlTransaction(bankAccount) { Amount = amount, BankAccountTransactionK = transactionK }; if (elem.Attribute("BankAccountTransactionFK") != null) { trans.CustomValues.Add(XmlTransaction.kXmlTransactionOldTransactonFK, elem.Attribute("BankAccountTransactionFK").Value); } trans.Message = elem.Attribute("Message") != null?elem.Attribute("Message").Value : null; bankAccount.AddTransaction(trans); transactionMap.Add(elem.Attribute("BankAccountTransactionK").Value, trans.BankAccountTransactionK); } Interlocked.Increment(ref i); } int txCount = Transactions.Count(); int x = 0; finalArgs.Percent = 0; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, finalArgs); } foreach (IBankAccount account in bankAccounts) { foreach (XmlTransaction trans in account.Transactions) { double pcc = (double)x / (double)txCount * 100; //assigns the transactionK according to the hashmap of the old key stored as custom values if (trans.CustomValues.ContainsKey(XmlTransaction.kXmlTransactionOldTransactonFK)) { object value = transactionMap[trans.CustomValues[XmlTransaction.kXmlTransactionOldTransactonFK]]; trans.BankAccountTransactionFK = value != null ? (long)value : -1L; } if (oldPercent != (int)pcc) { if (JournalLoadingPercentChanged != null) { finalArgs.Percent = (int)pcc; JournalLoadingPercentChanged(this, finalArgs); oldPercent = (int)pcc; } } Interlocked.Increment(ref x); trans.CustomValues.Clear(); trans.CustomValues = null; } } bankAccountMap = null; transactionMap = null; // CleanJournal(PurgeOptions.RemoveOrphanedAccounts | PurgeOptions.RemoveZeroBalanceAccounts); var accountCount = bankAccounts.Count; Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("\r\n Journal clean: {0} accounts and {1} transactions.", accountCount, Transactions.Count()); Console.ResetColor(); } } catch (Exception ex) { ConsoleEx.WriteAtEnd(2, ConsoleColor.Red, "[{0}]\r\n", SEconomyPlugin.Locale.StringOrDefault(79, "corrupt")); TShock.Log.ConsoleError(ex.ToString()); Console.WriteLine(SEconomyPlugin.Locale.StringOrDefault(80, "Your transaction journal appears to be corrupt and transactions have been lost.\n\nYou will start with a clean journal.\nYour old journal file has been move to SEconomy.journal.xml.gz.corrupt")); File.Move(path, path + "." + DateTime.Now.ToFileTime().ToString() + ".corrupt"); SaveJournal(); //yes there are valid uses for goto, don't judge me fool goto initPoint; } } finally { Console.WriteLine(); } return(true); }
static void journal_JournalLoadingPercentChanged(object sender, JournalLoadingPercentChangedEventArgs e) { ConsoleEx.WriteBar(e); }
protected void Process() { sec = new SEconomy(null); SEconomyPlugin.Instance = sec; XmlTransactionJournal journal = null; int oldPercent = 0, skipped = 0; Dictionary <long, long> oldNewTransactions = new Dictionary <long, long>(); JournalLoadingPercentChangedEventArgs args = new JournalLoadingPercentChangedEventArgs() { Label = "Accounts" }; sec.Configuration = Config.FromFile(Config.BaseDirectory + Path.DirectorySeparatorChar + "seconomy.config.json"); journal = new XmlTransactionJournal(sec, Config.BaseDirectory + Path.DirectorySeparatorChar + "SEconomy.journal.xml.gz"); JournalLoadingPercentChanged += journal_JournalLoadingPercentChanged; journal.JournalLoadingPercentChanged += journal_JournalLoadingPercentChanged; journal.LoadJournal(); Console.WriteLine(); if (DatabaseExists() == false) { Console.WriteLine("Your SEconomy database does not exist. Create it?"); Console.Write("[y/n] "); if (Console.ReadKey().KeyChar != 'y') { return; } CreateDatabase(); } Console.WriteLine("Your SEconomy database will be flushed. All accounts, and transactions will be deleted before the import."); Console.Write("Continue? [y/n] "); if (Console.ReadKey().KeyChar != 'y') { return; } Console.WriteLine(); Connection.Query(string.Format("DELETE FROM `{0}`.`bank_account`;", sec.Configuration.SQLConnectionProperties.DbName)); Connection.Query(string.Format("ALTER TABLE `{0}`.`bank_account` AUTO_INCREMENT 0;", sec.Configuration.SQLConnectionProperties.DbName)); Connection.Query(string.Format("DELETE FROM `{0}`.`bank_account_transaction`;", sec.Configuration.SQLConnectionProperties.DbName)); Connection.Query(string.Format("ALTER TABLE `{0}`.`bank_account_transaction` AUTO_INCREMENT 0;", sec.Configuration.SQLConnectionProperties.DbName)); Console.WriteLine("This will probably take a while...\r\n"); Console.WriteLine(); if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(null, args); } for (int i = 0; i < journal.BankAccounts.Count; i++) { IBankAccount account = journal.BankAccounts.ElementAtOrDefault(i); double percentComplete = (double)i / (double)journal.BankAccounts.Count * 100; long id = -1; string query = null; if (account == null) { continue; } query = @"INSERT INTO `bank_account` (user_account_name, world_id, flags, flags2, description, old_bank_account_k) VALUES (@0, @1, @2, @3, @4, @5);" ; try { Connection.QueryIdentity(query, out id, account.UserAccountName, account.WorldID, (int)account.Flags, 0, account.Description, account.BankAccountK); } catch (Exception ex) { TShock.Log.ConsoleError("[SEconomy MySQL] Sql error adding bank account: " + ex.ToString()); continue; } for (int t = 0; t < account.Transactions.Count(); t++) { long tranId = -1; ITransaction transaction = account.Transactions.ElementAtOrDefault(t); string txQuery = @"INSERT INTO `bank_account_transaction` (bank_account_fk, amount, message, flags, flags2, transaction_date_utc, old_bank_account_transaction_k) VALUES (@0, @1, @2, @3, @4, @5, @6);" ; try { Connection.QueryIdentity(txQuery, out tranId, id, (long)transaction.Amount, transaction.Message, (int)BankAccountTransactionFlags.FundsAvailable, (int)transaction.Flags2, transaction.TransactionDateUtc, transaction.BankAccountTransactionK); if (oldNewTransactions.ContainsKey(transaction.BankAccountTransactionK) == false) { oldNewTransactions[transaction.BankAccountTransactionK] = tranId; } } catch (Exception ex) { TShock.Log.ConsoleError("[SEconomy MySQL] Database error in BeginSourceTransaction: " + ex.Message); continue; } } if (oldPercent != (int)percentComplete) { args.Percent = (int)percentComplete; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(null, args); } oldPercent = (int)percentComplete; } } args.Label = "Reseed"; args.Percent = 0; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(null, args); } string updateQuery = @"update bank_account_transaction as OLDT inner join ( select bank_account_transaction_id, old_bank_account_transaction_k from bank_account_transaction ) as NEWT on OLDT.old_bank_account_transaction_k = NEWT.old_bank_account_transaction_k set OLDT.bank_account_transaction_fk = NEWT.bank_account_transaction_id" ; Connection.Query(updateQuery); Connection.Query("update `bank_account_transaction` set `old_bank_account_transaction_k` = null;"); args.Percent = 100; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(null, args); } Console.WriteLine("import complete", skipped); Console.WriteLine("Press any key to exit"); Console.Read(); }
protected void LoadBankAccounts() { long bankAccountCount = 0, tranCount = 0; int index = 0, oldPercent = 0; double percentComplete = 0; JournalLoadingPercentChangedEventArgs parsingArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Loading" }; try { if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } bankAccounts = new List <IBankAccount>(); bankAccountCount = Connection.QueryScalar <long>("select count(*) from `bank_account`;"); tranCount = Connection.QueryScalar <long>("select count(*) from `bank_account_transaction`;"); QueryResult bankAccountResult = Connection.QueryReader(@"select bank_account.*, sum(bank_account_transaction.amount) as balance from bank_account inner join bank_account_transaction on bank_account_transaction.bank_account_fk = bank_account.bank_account_id group by bank_account.bank_account_id;"); Action <int> percentCompleteFunc = i => { percentComplete = (double)i / (double)bankAccountCount * 100; if (oldPercent != (int)percentComplete) { parsingArgs.Percent = (int)percentComplete; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } oldPercent = (int)percentComplete; } }; foreach (var acc in bankAccountResult.AsEnumerable()) { MySQLBankAccount sqlAccount = null; sqlAccount = new MySQLBankAccount(this) { BankAccountK = acc.Get <long>("bank_account_id"), Description = acc.Get <string>("description"), Flags = (BankAccountFlags)Enum.Parse(typeof(BankAccountFlags), acc.Get <int>("flags").ToString()), UserAccountName = acc.Get <string>("user_account_name"), WorldID = acc.Get <long>("world_id"), Balance = acc.Get <long>("balance") }; //sqlAccount.SyncBalance(); lock (BankAccounts) { BankAccounts.Add(sqlAccount); } Interlocked.Increment(ref index); percentCompleteFunc(index); } parsingArgs.Percent = 100; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } // CleanJournal(PurgeOptions.RemoveOrphanedAccounts | PurgeOptions.RemoveZeroBalanceAccounts); Console.WriteLine("\r\n"); ConsoleEx.WriteLineColour(ConsoleColor.Cyan, " Journal clean: {0} accounts, {1} transactions", BankAccounts.Count(), tranCount); } catch (Exception ex) { TShock.Log.ConsoleError(" seconomy mysql: db error in LoadJournal: " + ex.Message); throw; } }
public void CleanJournal(PurgeOptions options) { long oldPercent = 0; List<string> userList = TShock.Users.GetUsers().Select(i => i.Name).ToList(); List<long> deleteList = new List<long>(); JournalLoadingPercentChangedEventArgs args = new JournalLoadingPercentChangedEventArgs() { Label = "Scrub", Percent = 0 }; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, args); } for (int i = 0; i < this.BankAccounts.Count; i++) { double pcc = (double)i / (double)BankAccounts.Count * 100; IBankAccount account = this.bankAccounts.ElementAtOrDefault(i); if ((options & PurgeOptions.RemoveOrphanedAccounts) == PurgeOptions.RemoveOrphanedAccounts && userList.Contains(account.UserAccountName) == false) { if (deleteList.Contains(account.BankAccountK) == false) { deleteList.Add(account.BankAccountK); userList.Remove(account.UserAccountName); continue; } } if ((options & PurgeOptions.RemoveZeroBalanceAccounts) == PurgeOptions.RemoveZeroBalanceAccounts && (account.Balance <= 0 && account.IsSystemAccount == false)) { if (deleteList.Contains(account.BankAccountK) == false) { deleteList.Add(account.BankAccountK); continue; } } if (oldPercent != (int)pcc) { args.Percent = (int)pcc; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, args); } oldPercent = (int)pcc; } } if (deleteList.Count > 0) { args.Label = "Clean"; args.Percent = 0; for (int i = 0; i < deleteList.Count; i++) { double pcc = (double)i / (double)deleteList.Count * 100; DeleteBankAccountAsync(deleteList[i]).Wait(); if (oldPercent != (int)pcc) { args.Percent = (int)pcc; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, args); } oldPercent = (int)pcc; } } } }
protected void LoadBankAccounts() { long bankAccountCount = 0, tranCount = 0; int index = 0, oldPercent = 0; double percentComplete = 0; JournalLoadingPercentChangedEventArgs parsingArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Loading" }; try { if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } bankAccounts = new List<IBankAccount>(); bankAccountCount = Connection.QueryScalar<long>("select count(*) from `bank_account`;"); tranCount = Connection.QueryScalar<long>("select count(*) from `bank_account_transaction`;"); QueryResult bankAccountResult = Connection.QueryReader(@"select bank_account.*, sum(bank_account_transaction.amount) as balance from bank_account inner join bank_account_transaction on bank_account_transaction.bank_account_fk = bank_account.bank_account_id group by bank_account.bank_account_id;"); Action<int> percentCompleteFunc = i => { percentComplete = (double)i / (double)bankAccountCount * 100; if (oldPercent != (int)percentComplete) { parsingArgs.Percent = (int)percentComplete; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } oldPercent = (int)percentComplete; } }; foreach (var acc in bankAccountResult.AsEnumerable()) { MySQLBankAccount sqlAccount = null; sqlAccount = new MySQLBankAccount(this) { BankAccountK = acc.Get<long>("bank_account_id"), Description = acc.Get<string>("description"), Flags = (BankAccountFlags)Enum.Parse(typeof(BankAccountFlags), acc.Get<int>("flags").ToString()), UserAccountName = acc.Get<string>("user_account_name"), WorldID = acc.Get<long>("world_id"), Balance = acc.Get<long>("balance") }; //sqlAccount.SyncBalance(); lock (BankAccounts) { BankAccounts.Add(sqlAccount); } Interlocked.Increment(ref index); percentCompleteFunc(index); } parsingArgs.Percent = 100; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } // CleanJournal(PurgeOptions.RemoveOrphanedAccounts | PurgeOptions.RemoveZeroBalanceAccounts); Console.WriteLine("\r\n"); ConsoleEx.WriteLineColour(ConsoleColor.Cyan, " Journal clean: {0} accounts, {1} transactions", BankAccounts.Count(), tranCount); } catch (Exception ex) { TShock.Log.ConsoleError(" seconomy mysql: db error in LoadJournal: " + ex.Message); throw; } }
public bool LoadJournal() { MemoryStream uncompressedData; JournalLoadingPercentChangedEventArgs parsingArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Loading" }; JournalLoadingPercentChangedEventArgs finalArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Verify" }; ConsoleEx.WriteLineColour(ConsoleColor.Cyan, " Using XML journal - {0}", path); try { byte[] fileData = new byte[0]; initPoint: try { fileData = File.ReadAllBytes(path); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.DarkCyan; if (ex is System.IO.FileNotFoundException || ex is System.IO.DirectoryNotFoundException) { TShock.Log.ConsoleInfo(" * It appears you do not have a journal yet, one will be created for you."); SaveJournal(); //yes there are valid uses for goto, don't judge me fool goto initPoint; } else if (ex is System.Security.SecurityException) { TShock.Log.ConsoleError(" * Access denied to the journal file. Check permissions."); } else { TShock.Log.ConsoleError(" * Loading your journal failed: " + ex.Message); } } try { uncompressedData = GZipDecompress(fileData); } catch { TShock.Log.ConsoleError(" * Decompression failed."); return false; } if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } try { Hashtable bankAccountMap = new Hashtable(); Hashtable transactionMap = new Hashtable(); //You can't use XDocument.Parse when you have an XDeclaration for some even dumber reason than the write //issue, XDocument has to be constructed from a .net 3.5 XmlReader in this case //or you get a parse exception complaining about literal content. using (XmlTextReader xmlStream = new XmlTextReader(uncompressedData)) { XDocument doc = XDocument.Load(xmlStream); var bankAccountList = doc.XPathSelectElements("/Journal/BankAccounts/BankAccount"); int bankAccountCount = bankAccountList.Count(); int i = 0; int oldPercent = 0; foreach (XElement elem in bankAccountList) { long bankAccountK = 0L; double percentComplete = (double)i / (double)bankAccountCount * 100; var account = new XmlBankAccount(this) { Description = elem.Attribute("Description").Value, UserAccountName = elem.Attribute("UserAccountName").Value, WorldID = long.Parse(elem.Attribute("WorldID").Value), Flags = (BankAccountFlags)Enum.Parse(typeof(BankAccountFlags), elem.Attribute("Flags").Value) }; if (!long.TryParse(elem.Attribute("BankAccountK").Value, out bankAccountK)) { //we've overwritten the old bank account key, add it to the old/new map bankAccountK = Interlocked.Increment(ref _bankAccountSeed); bankAccountMap.Add(elem.Attribute("BankAccountK").Value, bankAccountK); } account.BankAccountK = bankAccountK; //parse transactions under this node if (elem.Element("Transactions") != null) { foreach (XElement txElement in elem.Element("Transactions").Elements("Transaction")) { long transactionK = 0L; long transactionFK = 0; long amount = long.Parse(txElement.Attribute("Amount").Value); DateTime transactionDate; BankAccountTransactionFlags flags; long.TryParse(txElement.Attribute("BankAccountTransactionK").Value, out transactionK); long.TryParse(txElement.Attribute("BankAccountTransactionFK").Value, out transactionFK); DateTime.TryParse(txElement.Attribute("TransactionDateUtc").Value, out transactionDate); Enum.TryParse<BankAccountTransactionFlags>(txElement.Attribute("Flags").Value, out flags); //ignore orphaned transactions var trans = new XmlTransaction(account) { Amount = amount, BankAccountTransactionK = transactionK, BankAccountTransactionFK = transactionFK, TransactionDateUtc = transactionDate, Flags = flags }; trans.Message = txElement.Attribute("Message") != null ? txElement.Attribute("Message").Value : null; account.AddTransaction(trans); } } account.SyncBalance(); if (oldPercent != (int)percentComplete) { parsingArgs.Percent = (int)percentComplete; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } oldPercent = (int)percentComplete; } Interlocked.Increment(ref i); bankAccounts.Add(account); } Interlocked.Exchange(ref _bankAccountSeed, bankAccounts.Count() > 0 ? bankAccounts.Max(sum => sum.BankAccountK) : 0); //delete transactions with duplicate IDs var qAccounts = from summary in bankAccounts group summary by summary.BankAccountK into g where g.Count() > 1 select new { name = g.Key, count = g.Count() }; long[] duplicateAccounts = qAccounts.Select(pred => pred.name).ToArray(); int removedAccounts = bankAccounts.RemoveAll(pred => duplicateAccounts.Contains(pred.BankAccountK)); if (removedAccounts > 0) { TShock.Log.Warn("seconomy journal: removed " + removedAccounts + " accounts with duplicate IDs."); } //transactions in the old schema. int tranCount = doc.XPathSelectElements("/Journal/Transactions/Transaction").Count(); i = 0; //reset index foreach (XElement elem in doc.XPathSelectElements("/Journal/Transactions/Transaction")) { //Parallel.ForEach(doc.XPathSelectElements("/Journal/Transactions/Transaction"), (elem) => { double percentComplete = (double)i / (double)tranCount * 100; long bankAccountFK = 0L; long transactionK = 0L; long amount = long.Parse(elem.Attribute("Amount").Value); if (!long.TryParse(elem.Attribute("BankAccountFK").Value, out bankAccountFK)) { if (bankAccountMap.ContainsKey(elem.Attribute("BankAccountFK").Value)) { Interlocked.Exchange(ref bankAccountFK, (long)bankAccountMap[elem.Attribute("BankAccountFK").Value]); } } IBankAccount bankAccount = GetBankAccount(bankAccountFK); long.TryParse(elem.Attribute("BankAccountTransactionK").Value, out transactionK); //ignore orphaned transactions if (bankAccount != null) { var trans = new XmlTransaction(bankAccount) { Amount = amount, BankAccountTransactionK = transactionK }; if (elem.Attribute("BankAccountTransactionFK") != null) { trans.CustomValues.Add(XmlTransaction.kXmlTransactionOldTransactonFK, elem.Attribute("BankAccountTransactionFK").Value); } trans.Message = elem.Attribute("Message") != null ? elem.Attribute("Message").Value : null; bankAccount.AddTransaction(trans); transactionMap.Add(elem.Attribute("BankAccountTransactionK").Value, trans.BankAccountTransactionK); } Interlocked.Increment(ref i); } int txCount = Transactions.Count(); int x = 0; finalArgs.Percent = 0; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, finalArgs); } foreach (IBankAccount account in bankAccounts) { foreach (XmlTransaction trans in account.Transactions) { double pcc = (double)x / (double)txCount * 100; //assigns the transactionK according to the hashmap of the old key stored as custom values if (trans.CustomValues.ContainsKey(XmlTransaction.kXmlTransactionOldTransactonFK)) { object value = transactionMap[trans.CustomValues[XmlTransaction.kXmlTransactionOldTransactonFK]]; trans.BankAccountTransactionFK = value != null ? (long)value : -1L; } if (oldPercent != (int)pcc) { if (JournalLoadingPercentChanged != null) { finalArgs.Percent = (int)pcc; JournalLoadingPercentChanged(this, finalArgs); oldPercent = (int)pcc; } } Interlocked.Increment(ref x); trans.CustomValues.Clear(); trans.CustomValues = null; } } bankAccountMap = null; transactionMap = null; // CleanJournal(PurgeOptions.RemoveOrphanedAccounts | PurgeOptions.RemoveZeroBalanceAccounts); var accountCount = bankAccounts.Count; Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("\r\n Journal clean: {0} accounts and {1} transactions.", accountCount, Transactions.Count()); Console.ResetColor(); } } catch (Exception ex) { ConsoleEx.WriteAtEnd(2, ConsoleColor.Red, "[{0}]\r\n", SEconomyPlugin.Locale.StringOrDefault(79, "corrupt")); TShock.Log.ConsoleError(ex.ToString()); Console.WriteLine(SEconomyPlugin.Locale.StringOrDefault(80, "Your transaction journal appears to be corrupt and transactions have been lost.\n\nYou will start with a clean journal.\nYour old journal file has been move to SEconomy.journal.xml.gz.corrupt")); File.Move(path, path + "." + DateTime.Now.ToFileTime().ToString() + ".corrupt"); SaveJournal(); //yes there are valid uses for goto, don't judge me fool goto initPoint; } } finally { Console.WriteLine(); } return true; }