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); }
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; }