예제 #1
0
        public static void ReadTibia11Tabs(ReadMemoryResults results)
        {
            List <TabStructure> structures = new List <TabStructure>();

            Interlocked.Exchange(ref structures, tabStructures);
            foreach (TabStructure structure in structures)
            {
                IEnumerable <string> tabMessages = structure.GetMessages();
                SearchChunk(tabMessages, results);
            }
        }
예제 #2
0
        public static ReadMemoryResults ReadMemory()
        {
            ReadMemoryResults results = null;
            SYSTEM_INFO       sys_info;

            GetSystemInfo(out sys_info);

            IntPtr proc_min_address = sys_info.minimumApplicationAddress;
            IntPtr proc_max_address = sys_info.maximumApplicationAddress;

            long proc_min_address_l = (long)proc_min_address;
            long proc_max_address_l = (long)proc_max_address;

            Process[] processes = ProcessManager.GetTibiaProcesses();
            if (processes == null || processes.Length == 0)
            {
                return(null);
            }

            results = new ReadMemoryResults();
            skipDuplicateCommands = (FlashClient || !SettingsManager.getSettingBool("ScanInternalTabStructure")) && SettingsManager.getSettingBool("SkipDuplicateCommands");
            Dictionary <int, HashSet <long> > newWhitelistedAddresses = null;

            Interlocked.Exchange(ref newWhitelistedAddresses, whiteListedAddresses);

            if (ProcessManager.TibiaClientType == "Tibia11")
            {
                ReadTibia11Tabs(results);
            }
            else
            {
                foreach (Process process in processes)
                {
                    if (!FlashClient && SettingsManager.getSettingBool("ScanInternalTabStructure"))
                    {
                        ReadMemoryInternal(process, results);
                        UseInternalScan = true;
                    }
                    else
                    {
                        ReadMemoryWhiteList(process, newWhitelistedAddresses, FlashClient, results);
                        UseInternalScan = false;
                    }
                    process.Dispose();
                }
            }
            FinalCleanup(results);
            return(results);
        }
예제 #3
0
 private static void FinalCleanup(ReadMemoryResults res)
 {
     foreach (KeyValuePair <string, List <Tuple <string, string> > > kvp in res.commands)
     {
         string time = kvp.Key;
         if (res.itemDrops.ContainsKey(time))
         {
             foreach (Tuple <string, string> command in kvp.Value)
             {
                 if (res.itemDrops[time].Contains(command.Item2.Trim()))
                 {
                     res.itemDrops[time].Remove(command.Item2);
                 }
             }
         }
     }
 }
예제 #4
0
        public static void ReadMemoryInternal(Process process, ReadMemoryResults results)
        {
            int currentAddress = process.MainModule.BaseAddress.ToInt32();

            IntPtr ptr = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id);

            if (ptr == null)
            {
                return;
            }
            int processHandle = ptr.ToInt32();

            currentAddress += (int)MemoryReader.TabsBaseAddress;
            currentAddress  = MemoryReader.ReadInt32(currentAddress, processHandle);
            currentAddress  = MemoryReader.ReadInt32(currentAddress + 0x24, processHandle);
            currentAddress  = MemoryReader.ReadInt32(currentAddress + 0x10, processHandle);
            currentAddress  = MemoryReader.ReadInt32(currentAddress + 0x10, processHandle);
            int tabsAddress = MemoryReader.ReadInt32(currentAddress + 0x30, processHandle);

            //first tab node address is tabsAddress + 0x24
            int tabNodeAddress = MemoryReader.ReadInt32(tabsAddress + 0x24, processHandle);
            int tabCount       = 0;

            //repeat until tab node address = 0x0
            while (tabNodeAddress != 0x0)
            {
                tabCount++;

                //use 0x30 for longer name (possibly upto 30 bytes)
                //0x2C will use '...' for names longer than 15 chars
                int    tabNamePointer           = MemoryReader.ReadInt32(tabNodeAddress + 0x2C, processHandle);
                string tabName                  = MemoryReader.ReadString(tabNamePointer, 16, processHandle);
                int    tabMessagesDataStructure = MemoryReader.ReadInt32(tabNodeAddress + 0x24, processHandle);
                bool   serverLog                = tabName == "Server Log"; // only read log messages from the server log; only read chat messages from other tabs

                IEnumerable <string> tabMessages = ReadTabMessages(processHandle, tabMessagesDataStructure);
                SearchChunk(tabMessages, results, !serverLog, serverLog);

                //next tab node pointer is current tab node address + 0x10
                tabNodeAddress = MemoryReader.ReadInt32(tabNodeAddress + 0x10, processHandle);
            }
        }
예제 #5
0
 private static void FinalCleanup(ReadMemoryResults res)
 {
     foreach (KeyValuePair<string, List<Tuple<string, string>>> kvp in res.commands) {
         string time = kvp.Key;
         if (res.itemDrops.ContainsKey(time)) {
             foreach (Tuple<string, string> command in kvp.Value) {
                 if (res.itemDrops[time].Contains(command.Item2.Trim())) {
                     res.itemDrops[time].Remove(command.Item2);
                 }
             }
         }
     }
 }
예제 #6
0
 public static void ReadTibia11Tabs(ReadMemoryResults results)
 {
     List<TabStructure> structures = new List<TabStructure>();
     Interlocked.Exchange(ref structures, tabStructures);
     foreach(TabStructure structure in structures) {
         IEnumerable<string> tabMessages = structure.GetMessages();
         SearchChunk(tabMessages, results);
     }
 }
예제 #7
0
        public static void ReadMemoryWhiteList(Process process, Dictionary<int, HashSet<long>> newWhitelistedAddresses, bool flashClient, ReadMemoryResults results)
        {
            HashSet<long> whitelist;
            if (!newWhitelistedAddresses.TryGetValue(process.Id, out whitelist)) {
                return;
            }

            IntPtr processHandle = MemoryReader.OpenProcess(process);

            int bytesRead = 0;  // number of bytes read with ReadProcessMemory
            foreach (long addr in whitelist) {
                IntPtr proc_min_address = new IntPtr(addr);

                MEMORY_BASIC_INFORMATION mem_basic_info;
                VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28);

                if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) {
                    if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize) {
                        memoryBuffer = new byte[mem_basic_info.RegionSize];
                    }

                    // read everything in the buffer above
                    ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead);
                    // scan the memory for strings that start with timestamps and end with the null terminator ('\0')
                    IEnumerable<string> timestampLines;
                    if (!flashClient) {
                        timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead);
                    } else {
                        timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead);
                    }

                    SearchChunk(timestampLines, results);
                }
            }
        }
예제 #8
0
        public static void ReadMemoryInternal(Process process, ReadMemoryResults results)
        {
            int currentAddress = process.MainModule.BaseAddress.ToInt32();

            IntPtr ptr = MemoryReader.OpenProcess(process);
            if (ptr == null) {
                return;
            }
            int processHandle = ptr.ToInt32();

            currentAddress += (int)MemoryReader.TabsBaseAddress;
            currentAddress = MemoryReader.ReadInt32(currentAddress, processHandle);
            currentAddress = MemoryReader.ReadInt32(currentAddress + 0x24, processHandle);
            currentAddress = MemoryReader.ReadInt32(currentAddress + 0x10, processHandle);
            currentAddress = MemoryReader.ReadInt32(currentAddress + 0x10, processHandle);
            int tabsAddress = MemoryReader.ReadInt32(currentAddress + 0x30, processHandle);

            //first tab node address is tabsAddress + 0x24
            int tabNodeAddress = MemoryReader.ReadInt32(tabsAddress + 0x24, processHandle);
            int tabCount = 0;
            //repeat until tab node address = 0x0
            while (tabNodeAddress != 0x0) {
                tabCount++;

                //use 0x30 for longer name (possibly upto 30 bytes)
                //0x2C will use '...' for names longer than 15 chars
                int tabNamePointer = MemoryReader.ReadInt32(tabNodeAddress + 0x2C, processHandle);
                string tabName = MemoryReader.ReadString(tabNamePointer, 16, processHandle);
                int tabMessagesDataStructure = MemoryReader.ReadInt32(tabNodeAddress + 0x24, processHandle);
                bool serverLog = tabName == "Server Log"; // only read log messages from the server log; only read chat messages from other tabs

                IEnumerable<string> tabMessages = ReadTabMessages(processHandle, tabMessagesDataStructure);
                SearchChunk(tabMessages, results, !serverLog, serverLog);

                //next tab node pointer is current tab node address + 0x10
                tabNodeAddress = MemoryReader.ReadInt32(tabNodeAddress + 0x10, processHandle);
            }
        }
예제 #9
0
        public static ReadMemoryResults ReadMemory()
        {
            ReadMemoryResults results  = null;
            SYSTEM_INFO       sys_info = new SYSTEM_INFO();

            GetSystemInfo(out sys_info);

            IntPtr proc_min_address = sys_info.minimumApplicationAddress;
            IntPtr proc_max_address = sys_info.maximumApplicationAddress;

            long proc_min_address_l = (long)proc_min_address;
            long proc_max_address_l = (long)proc_max_address;

            Process[] processes = ProcessManager.GetTibiaProcesses();
            if (processes == null || processes.Length == 0)
            {
                // Tibia process could not be found, wait for a bit and return
                Thread.Sleep(250);
                return(null);
            }
            int scanSpeed = SettingsManager.getSettingInt("ScanSpeed");

            results     = new ReadMemoryResults();
            flashClient = ProcessManager.IsFlashClient();
            foreach (Process process in processes)
            {
                if (!whitelistedAddresses.ContainsKey(process.Id))
                {
                    continue;
                }
                List <long> whitelist = whitelistedAddresses[process.Id];

                IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id);

                int bytesRead = 0;  // number of bytes read with ReadProcessMemory
                for (int i = 0; i < whitelist.Count; i++)
                {
                    long addr = whitelist[i];
                    if (addr < 0)
                    {
                        continue;
                    }

                    proc_min_address = new IntPtr(addr);

                    MEMORY_BASIC_INFORMATION mem_basic_info;
                    VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28);

                    if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT)
                    {
                        if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize)
                        {
                            memoryBuffer = new byte[mem_basic_info.RegionSize];
                        }

                        // read everything in the buffer above
                        ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead);
                        // scan the memory for strings that start with timestamps and end with the null terminator ('\0')
                        IEnumerable <string> timestampLines;
                        if (!flashClient)
                        {
                            timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead);
                        }
                        else
                        {
                            timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead);
                        }

                        if (!SearchChunk(timestampLines, results))
                        {
                            whitelist[i] = -1;
                        }

                        // performance throttling sleep after every scan (depending on scanSpeed setting)
                        if (scanSpeed > 0)
                        {
                            Thread.Sleep(scanSpeed);
                        }
                    }
                }
                process.Dispose();
            }

            FinalCleanup(results);
            return(results);
        }
예제 #10
0
        private static void SearchChunk(IEnumerable <string> chunk, ReadMemoryResults res, bool readChatMessages = true, bool readLogMessages = true)
        {
            List <int> stamps = TimestampManager.getLatestStamps(3, ignoreStamp);

            foreach (string it in chunk)
            {
                string logMessage = it;
                string t          = logMessage.Substring(0, 5);
                int    hour       = int.Parse(logMessage.Substring(0, 2));
                int    minute     = int.Parse(logMessage.Substring(3, 2));
                if (!stamps.Contains(TimestampManager.getStamp(hour, minute)))
                {
                    continue;                                                            // the log message is not recent, so we skip parsing it
                }
                if (flashClient)
                {
                    // there is some inconsistency with log messages, certain log messages use "12:00: Message.", others use "12:00 Message"
                    // if there is a : after the timestamp we remove it
                    if (logMessage[5] == ':')
                    {
                        logMessage = logMessage.Remove(5, 1);
                    }
                }

                string message = logMessage.Substring(6); // message without timestamp
                if (readLogMessages)
                {
                    if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " You see ")
                    {
                        // the message contains "you see", so it's a look message
                        if (!res.lookMessages.ContainsKey(t))
                        {
                            res.lookMessages.Add(t, new List <string>());
                        }
                        res.lookMessages[t].Add(logMessage);
                        continue;
                    }
                    else if (message.Contains(':'))
                    {
                        if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ")   // loot drop message
                        {
                            if (!res.itemDrops.ContainsKey(t))
                            {
                                res.itemDrops.Add(t, new List <string>());
                            }
                            res.itemDrops[t].Add(logMessage);
                            continue;
                        }
                    }
                    else if (logMessage.Length > 17 && logMessage.Substring(5, 12) == " You gained ")
                    {
                        // the message is an experience string, "You gained x experience."
                        try {
                            int experience = int.Parse(logMessage.Substring(17).Split(' ')[0]);
                            if (!res.exp.ContainsKey(t))
                            {
                                res.exp.Add(t, experience);
                            }
                            else
                            {
                                res.exp[t] = res.exp[t] + experience;
                            }
                        } catch {
                        }
                        continue;
                    }
                    else if (logMessage.Length == 19 && logMessage.Substring(5, 14) == " You are dead.")
                    {
                        if (!res.deaths.ContainsKey(t))
                        {
                            res.deaths.Add(t, true);
                        }
                    }
                    else if (logMessage.Length > 18)
                    {
                        string[] split = message.Split(' ');
                        int      index = split.IndexOf("hitpoints");
                        if (index > 0)
                        {
                            int ind;
                            // damage log message (X loses Y hitpoints due to an attack by Z.)
                            int damage = 0;
                            if (!int.TryParse(split[index - 1], out damage))
                            {
                                continue;
                            }
                            string player;
                            if (logMessage.Substring(logMessage.Length - 12) == "your attack.")
                            {
                                // X lost Y hitpoints because of your attack.
                                // attacker is the player himself
                                player = "You";
                            }
                            else if (split.Contains("by"))
                            {
                                // X lost Y hitpoints because of an attack by Z.
                                // Z is the attacker => after the word "by"
                                player = "";
                                ind    = split.IndexOf("by") + 1;
                                for (int i = ind; i < split.Length; i++)
                                {
                                    player = (player == "" ? player : player + " ") + split[i];
                                }
                            }
                            else
                            {
                                continue;
                            }
                            string splitTerm;
                            if (split.Contains("loses"))
                            {
                                splitTerm = "loses";
                            }
                            else if (split.Contains("lose"))
                            {
                                splitTerm = "lose";
                            }
                            else
                            {
                                continue;
                            }
                            ind = split.IndexOf(splitTerm);
                            string target = "";
                            for (int i = 0; i < ind; i++)
                            {
                                target = (target == "" ? target : target + " ") + split[i];
                            }
                            if (!res.damageDealt.ContainsKey(player))
                            {
                                res.damageDealt.Add(player, new Dictionary <string, DamageEntry>());
                            }
                            DamageEntry damageEntry;
                            if (!res.damageDealt[player].ContainsKey(t))
                            {
                                damageEntry        = new DamageEntry();
                                damageEntry.damage = damage;
                                damageEntry.targetDamage.Add(target, damage);
                                res.damageDealt[player].Add(t, damageEntry);
                            }
                            else
                            {
                                damageEntry         = res.damageDealt[player][t];
                                damageEntry.damage += damage;
                                if (damageEntry.targetDamage.ContainsKey(target))
                                {
                                    damageEntry.targetDamage[target] += damage;
                                }
                                else
                                {
                                    damageEntry.targetDamage.Add(target, damage);
                                }
                            }
                            continue;
                        }
                        else
                        {
                            index = split.IndexOf("hitpoints.");
                            if (index > 0)
                            {
                                // heal log message (X healed Y for Z hitpoints.)
                                int healing = 0;
                                if (!int.TryParse(split[index - 1], out healing))
                                {
                                    continue;
                                }

                                int forIndex = split.IndexOf("for");
                                if (forIndex <= 0)
                                {
                                    continue;
                                }


                                string splitTerm;
                                if (split.Contains("heal"))
                                {
                                    splitTerm = "heal";
                                }
                                else if (split.Contains("healed"))
                                {
                                    splitTerm = "healed";
                                }
                                else
                                {
                                    continue;
                                }
                                int healIndex = split.IndexOf(splitTerm);
                                if (healIndex >= forIndex)
                                {
                                    continue;
                                }

                                string source = "";
                                for (int i = 0; i < healIndex; i++)
                                {
                                    if (split[i] == "was" || split[i] == "were")
                                    {
                                        break;
                                    }
                                    if (split[i] == "by")
                                    {
                                        continue;
                                    }
                                    source = (source == "" ? source : source + " ") + split[i];
                                }
                                string target = "";
                                for (int i = healIndex + 1; i < forIndex; i++)
                                {
                                    if (split[i] == "was" || split[i] == "were")
                                    {
                                        break;
                                    }
                                    if (split[i] == "by")
                                    {
                                        continue;
                                    }
                                    target = (target == "" ? target : target + " ") + split[i];
                                }
                                if (target == "yourself" || target == "itself" || target == "himself" || target == "herself")
                                {
                                    target = source;
                                }

                                if (target.Length == 0 || source.Length == 0)
                                {
                                    continue;
                                }

                                if (split.Contains("by"))
                                {
                                    // X healed Y for Z. => X is the source and Y is the target (default)
                                    // X was healed by Y for Z. => X is the target and Y is the source, so swap source and target
                                    string temp = source;
                                    source = target;
                                    target = temp;
                                }

                                if (!res.healingDone.ContainsKey(source))
                                {
                                    res.healingDone.Add(source, new Dictionary <string, DamageEntry>());
                                }
                                DamageEntry healingEntry;
                                if (!res.healingDone[source].ContainsKey(t))
                                {
                                    healingEntry        = new DamageEntry();
                                    healingEntry.damage = healing;
                                    healingEntry.targetDamage.Add(target, healing);
                                    res.healingDone[source].Add(t, healingEntry);
                                }
                                else
                                {
                                    healingEntry         = res.healingDone[source][t];
                                    healingEntry.damage += healing;
                                    if (healingEntry.targetDamage.ContainsKey(target))
                                    {
                                        healingEntry.targetDamage[target] += healing;
                                    }
                                    else
                                    {
                                        healingEntry.targetDamage.Add(target, healing);
                                    }
                                }
                            }
                            else if (logMessage.Substring(5, 14) == " You advanced " && logMessage.Contains("level", StringComparison.OrdinalIgnoreCase))
                            {
                                // advancement log message (You advanced from level x to level x + 1.)
                                if (logMessage[logMessage.Length - 1] == '.')
                                {
                                    if (GlobalDataManager.AddLevelAdvance(logMessage))
                                    {
                                        res.newAdvances.Add(logMessage);
                                    }
                                    continue;
                                }
                            }
                            else if (logMessage.Substring(5, 7) == " Using " && logMessage.Substring(logMessage.Length - 3, 3) == "...")
                            {
                                // using log message (Using one of X items...)
                                var values = Parser.ParseUsingMessage(logMessage);
                                if (!res.usingMessages.ContainsKey(values.Item1))
                                {
                                    res.usingMessages.Add(values.Item1, new Dictionary <string, HashSet <int> >());
                                }
                                if (!res.usingMessages[values.Item1].ContainsKey(t))
                                {
                                    res.usingMessages[values.Item1].Add(t, new HashSet <int>());
                                }
                                res.usingMessages[values.Item1][t].Add(values.Item2);
                                continue;
                            }
                            else
                            {
                                foreach (Event ev in StorageManager.eventIdMap.Values)
                                {
                                    foreach (string evMessage in ev.eventMessages)
                                    {
                                        if (logMessage.Length == evMessage.Length + 6 && logMessage.Contains(evMessage.Trim(), StringComparison.OrdinalIgnoreCase))
                                        {
                                            res.eventMessages.Add(new Tuple <Event, string>(ev, logMessage));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if (readChatMessages)
                {
                    if (message.Contains(':'))
                    {
                        if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ")   // loot drop message
                        {
                            continue;
                        }
                        else     // if the message contains the ':' symbol but is not a loot drop message, it is a chat message, i.e. a command or url
                                 // we only split at most once, because the chat message can contain the ':' symbol as well and we don't want to discard that
                        {
                            string[] split   = message.Split(new char[] { ':' }, 2);
                            string   command = split[1];
                            // now get the player name, we have to discard the level that is between brackets
                            // players can also have spaces in their name, so we take that into account
                            string[] playersplit = split[0].Split(' ');
                            string   player      = "";
                            foreach (string str in playersplit)
                            {
                                if (str.Contains('['))
                                {
                                    break;
                                }
                                player = (player == "" ? player : player + " ") + str;
                            }
                            if (player == "http" || player == "https")
                            {
                                continue;                                        // I don't remember why we do this, possible http link in a log message? not sure
                            }
                            if (command.Contains('@'))
                            {
                                // @ symbol symbolizes a command, so if there is an @ symbol, we treat the string as a command
                                if (!res.commands.ContainsKey(t))
                                {
                                    res.commands.Add(t, new List <Tuple <string, string> >());
                                }
                                res.commands[t].Add(new Tuple <string, string>(player, command));
                            }
                            else if (command.Contains("www") || command.Contains("http") || command.Contains(".com") || command.Contains(".net") || command.Contains(".tv") || command.Contains(".br"))
                            {
                                // check if the command is an url, we aren't really smart about this, just check for a couple of common url-like things
                                if (!res.urls.ContainsKey(t))
                                {
                                    res.urls.Add(t, new List <Tuple <string, string> >());
                                }
                                res.urls[t].Add(new Tuple <string, string>(player, command));
                            }
                        }
                    }
                }
            }
        }
예제 #11
0
        private ParseMemoryResults ParseLogResults(ReadMemoryResults res)
        {
            if (res == null) return null;
            ParseMemoryResults o = new ParseMemoryResults();
            // first we add the new parsed damage logs to the totalDamageResults
            foreach (KeyValuePair<string, Dictionary<string, int>> kvp in res.damageDealt) {
                string player = kvp.Key;
                Dictionary<string, int> playerDamage = kvp.Value;
                if (!totalDamageResults.ContainsKey(player)) totalDamageResults.Add(player, new Dictionary<string, int>());
                foreach (KeyValuePair<string, int> kvp2 in playerDamage) {
                    string timestamp = kvp2.Key;
                    int damage = kvp2.Value;
                    // if the damage for the given timestamp does not exist yet, add it
                    if (!totalDamageResults[player].ContainsKey(timestamp)) totalDamageResults[player].Add(timestamp, damage);
                    // if it does exist, select the biggest of the two
                    // the reason we select the biggest of the two is:
                    // - if the timestamp is 'the current time', totalDamageResults may hold an old value, so we update it
                    // - if timestamp is old, a part of the log for the time could have already been removed (because the log was full)
                    //    so the 'new' damage is only part of the damage for this timestamp
                    else if (totalDamageResults[player][timestamp] < damage) totalDamageResults[player][timestamp] = damage;
                }
            }
            // now that we have updated the damage results, fill in the DPS meter, we use damage from the last 15 minutes for this
            List<string> times = getLatestTimes(15);
            foreach (KeyValuePair<string, Dictionary<string, int>> kvp in totalDamageResults) {
                string player = kvp.Key;
                int damage = 0;
                int minutes = 0;
                foreach (string t in times) {
                    if (totalDamageResults[player].ContainsKey(t)) {
                        damage += totalDamageResults[player][t];
                        minutes++;
                    }
                }
                if (damage > 0) {
                    o.damagePerSecond.Add(player, new Tuple<int, int>(damage, minutes));
                }
            }

            // similar to damage, we keep a totalExperienceResults list
            // first update it with the new information
            int newExperience = 0;
            foreach (KeyValuePair<string, int> kvp in res.exp) {
                string time = kvp.Key;
                int experience = kvp.Value;
                if (!totalExperienceResults.ContainsKey(time)) {
                    totalExperienceResults.Add(time, experience);
                    newExperience += experience;
                } else if (totalExperienceResults[time] < experience) {
                    newExperience += experience - totalExperienceResults[time];
                    totalExperienceResults[time] = experience;
                }
            }
            // now compute the experience per hour
            // we use the same formula Tibia itself does so we get the same value
            // this formula is basically, take the experience in the last 15 minutes and multiply it by 4
            foreach (string t in times) {
                if (totalExperienceResults.ContainsKey(t)) o.expPerHour += totalExperienceResults[t];
            }
            o.expPerHour *= 4;

            // Parse event messages
            foreach (Tuple<Event, string> tpl in res.eventMessages) {
                if (!eventMessages.Contains(tpl.Item2)) {
                    eventMessages.Add(tpl.Item2);
                    o.newEventMessages.Add(tpl);
                }
            }

            // Update the look information
            foreach (KeyValuePair<string, List<string>> kvp in res.lookMessages) {
                string t = kvp.Key;
                List<string> currentMessages = kvp.Value;
                if (!totalLooks.ContainsKey(t)) totalLooks[t] = new List<string>();
                if (currentMessages.Count > totalLooks[t].Count) {
                    List<string> unseenLooks = new List<string>();
                    List<string> lookList = totalLooks[t].ToArray().ToList();
                    foreach (string lookMessage in currentMessages) {
                        if (!totalLooks[t].Contains(lookMessage)) {
                            unseenLooks.Add(lookMessage);
                            o.newLooks.Add(lookMessage);
                        } else {
                            totalLooks[t].Remove(lookMessage);
                        }
                    }
                    lookList.AddRange(unseenLooks);
                    totalLooks[t] = lookList;
                }
            }

            // Update death information
            foreach (KeyValuePair<string, bool> kvp in res.deaths) {
                if (!totalDeaths.ContainsKey(kvp.Key)) {
                    totalDeaths.Add(kvp.Key, false);
                }
                if (kvp.Value && !totalDeaths[kvp.Key]) {
                    o.death = true;
                    totalDeaths[kvp.Key] = true;
                }
            }

            // now parse any new commands given by users
            foreach (KeyValuePair<string, List<Tuple<string, string>>> kvp in res.commands) {
                string t = kvp.Key;
                List<Tuple<string, string>> currentCommands = kvp.Value;
                if (!totalCommands.ContainsKey(t)) totalCommands[t] = new List<Tuple<string, string>>();
                if (currentCommands.Count > totalCommands[t].Count) {
                    List<Tuple<string, string>> unseenCommands = new List<Tuple<string, string>>();
                    List<Tuple<string, string>> commandsList = totalCommands[t].ToArray().ToList(); // create a copy of the list
                    foreach (Tuple<string, string> command in currentCommands) {
                        if (!totalCommands[t].Contains(command)) {
                            unseenCommands.Add(command);
                            string player = command.Item1;
                            string cmd = command.Item2;
                            if (SettingsManager.getSetting("Names").Contains(player)) {
                                o.newCommands.Add(cmd);
                            }
                        } else {
                            totalCommands[t].Remove(command);
                        }
                    }
                    commandsList.AddRange(unseenCommands);
                    totalCommands[t] = commandsList;
                }
            }

            // check new urls
            foreach (KeyValuePair<string, List<Tuple<string, string>>> kvp in res.urls) {
                string t = kvp.Key;
                List<Tuple<string, string>> currentURLs = kvp.Value;
                if (!totalURLs.ContainsKey(t)) {
                    totalURLs.Add(t, currentURLs);
                } else if (currentURLs.Count > totalURLs[t].Count) {
                    totalURLs[t] = currentURLs;
                }
            }

            ParseLootMessages(activeHunt, res.itemDrops, o.newItems, true, true);
            activeHunt.totalExp += newExperience;

            readWatch.Stop();
            if (newExperience == 0) {
                if (ticksSinceExperience < 120) {
                    ticksSinceExperience += readWatch.Elapsed.TotalSeconds;
                }
            } else {
                ticksSinceExperience = 0;
            }
            if (ticksSinceExperience < 120) {
                activeHunt.totalTime += readWatch.Elapsed.TotalSeconds;
            }
            readWatch.Restart();
            saveHunts();
            return o;
        }
예제 #12
0
        public static ReadMemoryResults ReadMemory()
        {
            ReadMemoryResults results = null;
            SYSTEM_INFO sys_info = new SYSTEM_INFO();
            GetSystemInfo(out sys_info);

            IntPtr proc_min_address = sys_info.minimumApplicationAddress;
            IntPtr proc_max_address = sys_info.maximumApplicationAddress;

            long proc_min_address_l = (long)proc_min_address;
            long proc_max_address_l = (long)proc_max_address;
            Process[] processes = ProcessManager.GetTibiaProcesses();
            if (processes == null || processes.Length == 0) {
                // Tibia process could not be found, wait for a bit and return
                Thread.Sleep(250);
                return null;
            }
            int scanSpeed = SettingsManager.getSettingInt("ScanSpeed");
            results = new ReadMemoryResults();
            flashClient = ProcessManager.IsFlashClient();
            foreach(Process process in processes) {
                if (!whitelistedAddresses.ContainsKey(process.Id)) continue;
                List<long> whitelist = whitelistedAddresses[process.Id];

                IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id);

                int bytesRead = 0;  // number of bytes read with ReadProcessMemory
                for (int i = 0; i < whitelist.Count; i++) {
                    long addr = whitelist[i];
                    if (addr < 0) continue;

                    proc_min_address = new IntPtr(addr);

                    MEMORY_BASIC_INFORMATION mem_basic_info;
                    VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28);

                    if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) {
                        if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize)
                        {
                            memoryBuffer = new byte[mem_basic_info.RegionSize];
                        }

                        // read everything in the buffer above
                        ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead);
                        // scan the memory for strings that start with timestamps and end with the null terminator ('\0')
                        IEnumerable<string> timestampLines;
                        if (!flashClient) {
                            timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead);
                        } else {
                            timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead);
                        }

                        if (!SearchChunk(timestampLines, results)) {
                            whitelist[i] = -1;
                        }

                        // performance throttling sleep after every scan (depending on scanSpeed setting)
                        if (scanSpeed > 0) {
                            Thread.Sleep(scanSpeed);
                        }
                    }
                }
                process.Dispose();
            }

            FinalCleanup(results);
            return results;
        }
예제 #13
0
        public static bool ScanMemory()
        {
            ReadMemoryResults  readMemoryResults  = ReadMemoryManager.ReadMemory();
            ParseMemoryResults parseMemoryResults = Parser.ParseLogResults(readMemoryResults);

            if (parseMemoryResults != null)
            {
                lastResults = parseMemoryResults;
                if (parseMemoryResults.newDamage)
                {
                    GlobalDataManager.UpdateDamage();
                }
            }

            if (readMemoryResults != null && readMemoryResults.newAdvances.Count > 0)
            {
                if (SettingsManager.getSettingBool("AutoScreenshotAdvance"))
                {
                    MainForm.mainForm.Invoke((MethodInvoker) delegate {
                        ScreenshotManager.saveScreenshot("Advance", ScreenshotManager.takeScreenshot());
                    });
                }
                if (SettingsManager.getSettingBool("CopyAdvances"))
                {
                    foreach (object obj in readMemoryResults.newAdvances)
                    {
                        MainForm.mainForm.Invoke((MethodInvoker) delegate {
                            Clipboard.SetText(obj.ToString());
                        });
                    }
                }
                readMemoryResults.newAdvances.Clear();
            }

            if (parseMemoryResults != null && parseMemoryResults.death)
            {
                if (SettingsManager.getSettingBool("AutoScreenshotDeath"))
                {
                    MainForm.mainForm.Invoke((MethodInvoker) delegate {
                        ScreenshotManager.saveScreenshot("Death", ScreenshotManager.takeScreenshot());
                    });
                }
                parseMemoryResults.death = false;
            }

            if (parseMemoryResults != null)
            {
                if (parseMemoryResults.newEventMessages.Count > 0)
                {
                    if (SettingsManager.getSettingBool("EnableEventNotifications"))
                    {
                        foreach (Tuple <Event, string> tpl in parseMemoryResults.newEventMessages)
                        {
                            Event    ev = tpl.Item1;
                            Creature cr = StorageManager.getCreature(ev.creatureid);
                            MainForm.mainForm.Invoke((MethodInvoker) delegate {
                                if (!SettingsManager.getSettingBool("UseRichNotificationType"))
                                {
                                    PopupManager.ShowSimpleNotification("Event in " + ev.location, tpl.Item2, cr.image);
                                }
                                else
                                {
                                    PopupManager.ShowSimpleNotification(new SimpleTextNotification(cr.image, "Event in " + ev.location, tpl.Item2));
                                }
                            });
                        }
                    }
                    parseMemoryResults.newEventMessages.Clear();
                }
            }

            if (SettingsManager.getSettingBool("LookMode") && readMemoryResults != null)
            {
                foreach (string msg in parseMemoryResults.newLooks)
                {
                    string itemName = Parser.parseLookItem(msg).ToLower();
                    if (StorageManager.itemExists(itemName))
                    {
                        MainForm.mainForm.Invoke((MethodInvoker) delegate {
                            CommandManager.ExecuteCommand("item@" + itemName);
                        });
                    }
                    else if (StorageManager.creatureExists(itemName) ||
                             (itemName.Contains("dead ") && (itemName = itemName.Replace("dead ", "")) != null && StorageManager.creatureExists(itemName)) ||
                             (itemName.Contains("slain ") && (itemName = itemName.Replace("slain ", "")) != null && StorageManager.creatureExists(itemName)))
                    {
                        MainForm.mainForm.Invoke((MethodInvoker) delegate {
                            CommandManager.ExecuteCommand("creature@" + itemName);
                        });
                    }
                    else
                    {
                        NPC npc = StorageManager.getNPC(itemName);
                        if (npc != null)
                        {
                            MainForm.mainForm.Invoke((MethodInvoker) delegate {
                                CommandManager.ExecuteCommand("npc@" + itemName);
                            });
                        }
                    }
                }
                parseMemoryResults.newLooks.Clear();
            }

            List <string> commands = parseMemoryResults == null ? new List <string>() : parseMemoryResults.newCommands.ToList();

            commands.Reverse();

            foreach (string command in commands)
            {
                MainForm.mainForm.Invoke((MethodInvoker) delegate {
                    if (!CommandManager.ExecuteCommand(command, parseMemoryResults) && SettingsManager.getSettingBool("EnableUnrecognizedNotifications"))
                    {
                        if (!SettingsManager.getSettingBool("UseRichNotificationType"))
                        {
                            PopupManager.ShowSimpleNotification("Unrecognized command", "Unrecognized command: " + command, StyleManager.GetImage("tibia.png"));
                        }
                        else
                        {
                            PopupManager.ShowSimpleNotification(new SimpleTextNotification(null, "Unrecognized command", "Unrecognized command: " + command));
                        }
                    }
                });
            }
            if (parseMemoryResults != null)
            {
                if (parseMemoryResults.newItems.Count > 0)
                {
                    MainForm.mainForm.Invoke((MethodInvoker) delegate {
                        LootDatabaseManager.UpdateLoot();
                    });
                }
                foreach (Tuple <Creature, List <Tuple <Item, int> > > tpl in parseMemoryResults.newItems)
                {
                    Creature cr = tpl.Item1;
                    List <Tuple <Item, int> > items = tpl.Item2;
                    bool showNotification           = PopupManager.ShowDropNotification(tpl);
                    if (showNotification)
                    {
                        if (!SettingsManager.getSettingBool("UseRichNotificationType"))
                        {
                            Console.WriteLine("Rich Notification");
                            PopupManager.ShowSimpleNotification(cr.displayname, cr.displayname + " dropped a valuable item.", cr.image);
                        }
                        else
                        {
                            MainForm.mainForm.Invoke((MethodInvoker) delegate {
                                PopupManager.ShowSimpleNotification(new SimpleLootNotification(cr, items));
                            });
                        }

                        if (SettingsManager.getSettingBool("AutoScreenshotItemDrop"))
                        {
                            // Take a screenshot if Tibialyzer is set to take screenshots of valuable loot
                            Bitmap screenshot = ScreenshotManager.takeScreenshot();
                            if (screenshot == null)
                            {
                                continue;
                            }
                            // Add a notification to the screenshot
                            SimpleLootNotification screenshotNotification = new SimpleLootNotification(cr, items);
                            Bitmap notification = new Bitmap(screenshotNotification.Width, screenshotNotification.Height);
                            screenshotNotification.DrawToBitmap(notification, new Rectangle(0, 0, screenshotNotification.Width, screenshotNotification.Height));
                            foreach (Control c in screenshotNotification.Controls)
                            {
                                c.DrawToBitmap(notification, new Rectangle(c.Location, c.Size));
                            }
                            screenshotNotification.Dispose();
                            int widthOffset  = notification.Width + 10;
                            int heightOffset = notification.Height + 10;
                            if (screenshot.Width > widthOffset && screenshot.Height > heightOffset)
                            {
                                using (Graphics gr = Graphics.FromImage(screenshot)) {
                                    gr.DrawImage(notification, new Point(screenshot.Width - widthOffset, screenshot.Height - heightOffset));
                                }
                            }
                            notification.Dispose();
                            MainForm.mainForm.Invoke((MethodInvoker) delegate {
                                ScreenshotManager.saveScreenshot("Loot", screenshot);
                            });
                        }
                    }
                }
            }
            return(readMemoryResults != null);
        }
예제 #14
0
        public static ParseMemoryResults ParseLogResults(ReadMemoryResults res)
        {
            if (res == null)
            {
                return(null);
            }
            ParseMemoryResults o = new ParseMemoryResults();

            // first we add the new parsed damage logs to the totalDamageResults
            o.newDamage = GlobalDataManager.UpdateDamageInformation(res.damageDealt);
            // now that we have updated the damage results, fill in the DPS meter, we use damage from the last 15 minutes for this
            List <string> times = TimestampManager.getLatestTimes(15);

            GlobalDataManager.GenerateDamageResults(o.damagePerSecond, times);

            // similar to damage, we keep a totalExperienceResults list
            // first update it with the new information
            int newExperience = GlobalDataManager.UpdateExperience(res.exp);

            // now compute the experience per hour
            // we use the same formula Tibia itself does so we get the same value
            // this formula is basically, take the experience in the last 15 minutes and multiply it by 4
            o.expPerHour = GlobalDataManager.GetExperiencePerHour();

            // Parse event messages
            foreach (Tuple <Event, string> newEvent in GlobalDataManager.UpdateEventInformation(res.eventMessages))
            {
                o.newEventMessages.Add(newEvent);
            }

            // Update the look information
            foreach (string newLook in GlobalDataManager.UpdateLookInformation(res.lookMessages))
            {
                o.newLooks.Add(newLook);
            }

            // Update death information
            o.death = GlobalDataManager.UpdateDeaths(res.deaths);

            // now parse any new commands given by users
            foreach (string newCommand in GlobalDataManager.UpdateCommands(res.commands))
            {
                o.newCommands.Add(newCommand);
            }

            // check new urls
            GlobalDataManager.UpdateURLs(res.urls);


            HuntManager.AddUsedItems(HuntManager.activeHunt, res.usingMessages);
            Parser.ParseLootMessages(HuntManager.activeHunt, res.itemDrops, o.newItems, true, true);
            HuntManager.activeHunt.totalExp += newExperience;

            readWatch.Stop();
            if (newExperience == 0)
            {
                if (ticksSinceExperience < 120)
                {
                    ticksSinceExperience += readWatch.Elapsed.TotalSeconds;
                }
            }
            else
            {
                ticksSinceExperience = 0;
            }
            if (ticksSinceExperience < 120)
            {
                HuntManager.activeHunt.totalTime += readWatch.Elapsed.TotalSeconds;
            }
            readWatch.Restart();
            HuntManager.SaveHunts();
            return(o);
        }
예제 #15
0
        private static bool SearchChunk(IEnumerable <string> chunk, ReadMemoryResults res)
        {
            List <int> stamps      = TimestampManager.getLatestStamps(3, ignoreStamp);
            bool       chunksExist = false;

            foreach (string it in chunk)
            {
                chunksExist = true;
                string logMessage = it;
                string t          = logMessage.Substring(0, 5);
                int    hour       = int.Parse(logMessage.Substring(0, 2));
                int    minute     = int.Parse(logMessage.Substring(3, 2));
                if (!stamps.Contains(TimestampManager.getStamp(hour, minute)))
                {
                    continue;                                                            // the log message is not recent, so we skip parsing it
                }
                if (flashClient)
                {
                    // there is some inconsistency with log messages, certain log messages use "12:00: Message.", others use "12:00 Message"
                    // if there is a : after the timestamp we remove it
                    if (logMessage[5] == ':')
                    {
                        logMessage = logMessage.Remove(5, 1);
                    }
                }
                string message = logMessage.Substring(6); // message without timestamp
                if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " You see ")
                {
                    // the message contains "you see", so it's a look message
                    if (!res.lookMessages.ContainsKey(t))
                    {
                        res.lookMessages.Add(t, new List <string>());
                    }
                    res.lookMessages[t].Add(logMessage);
                }
                else if (message.Contains(':'))
                {
                    if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ")   // loot drop message
                    {
                        if (!res.itemDrops.ContainsKey(t))
                        {
                            res.itemDrops.Add(t, new List <string>());
                        }
                        res.itemDrops[t].Add(logMessage);
                    }
                    else     // if the message contains the ':' symbol but is not a loot drop message, it is a chat message, i.e. a command or url
                             // we only split at most once, because the chat message can contain the ':' symbol as well and we don't want to discard that
                    {
                        string[] split   = message.Split(new char[] { ':' }, 2);
                        string   command = split[1];
                        // now get the player name, we have to discard the level that is between brackets
                        // players can also have spaces in their name, so we take that into account
                        string[] playersplit = split[0].Split(' ');
                        string   player      = "";
                        foreach (string str in playersplit)
                        {
                            if (str.Contains('['))
                            {
                                break;
                            }
                            player = (player == "" ? player : player + " ") + str;
                        }
                        if (player == "http" || player == "https")
                        {
                            continue;                                        // I don't remember why we do this, possible http link in a log message? not sure
                        }
                        if (command.Contains('@'))
                        {
                            // @ symbol symbolizes a command, so if there is an @ symbol, we treat the string as a command
                            if (!res.commands.ContainsKey(t))
                            {
                                res.commands.Add(t, new List <Tuple <string, string> >());
                            }
                            res.commands[t].Add(new Tuple <string, string>(player, command));
                        }
                        else if (command.Contains("www") || command.Contains("http") || command.Contains(".com") || command.Contains(".net") || command.Contains(".tv") || command.Contains(".br"))
                        {
                            // check if the command is an url, we aren't really smart about this, just check for a couple of common url-like things
                            if (!res.urls.ContainsKey(t))
                            {
                                res.urls.Add(t, new List <Tuple <string, string> >());
                            }
                            res.urls[t].Add(new Tuple <string, string>(player, command));
                        }
                    }
                }
                else if (logMessage.Length > 17 && logMessage.Substring(5, 12) == " You gained ")
                {
                    // the message is an experience string, "You gained x experience."
                    try {
                        int experience = int.Parse(logMessage.Substring(17).Split(' ')[0]);
                        if (!res.exp.ContainsKey(t))
                        {
                            res.exp.Add(t, experience);
                        }
                        else
                        {
                            res.exp[t] = res.exp[t] + experience;
                        }
                    } catch {
                        continue;
                    }
                }
                else if (logMessage.Length == 19 && logMessage.Substring(5, 14) == " You are dead.")
                {
                    if (!res.deaths.ContainsKey(t))
                    {
                        res.deaths.Add(t, true);
                    }
                }
                else if (logMessage.Length > 18)
                {
                    string[] split = message.Split(' ');
                    if (split.Contains("hitpoints") && split.ToList().IndexOf("hitpoints") > 0)
                    {
                        // damage log message (X loses Y hitpoints due to an attack by Z.)
                        int damage = 0;
                        if (!int.TryParse(split[split.ToList().IndexOf("hitpoints") - 1], out damage))
                        {
                            continue;
                        }
                        string player;
                        if (logMessage.Substring(logMessage.Length - 12) == "your attack.")
                        {
                            // X lost Y hitpoints because of your attack.
                            // attacker is the player himself
                            player = "You";
                        }
                        else
                        {
                            // X lost Y hitpoints because of an attack by Z.
                            // Z is the attacker => after the word "by"
                            if (!split.Contains("by"))
                            {
                                continue;
                            }
                            player = "";
                            int ind = split.ToList().IndexOf("by") + 1;
                            for (int i = ind; i < split.Length; i++)
                            {
                                player = (player == "" ? player : player + " ") + split[i];
                            }
                        }
                        if (!res.damageDealt.ContainsKey(player))
                        {
                            res.damageDealt.Add(player, new Dictionary <string, int>());
                        }
                        if (!res.damageDealt[player].ContainsKey(t))
                        {
                            res.damageDealt[player].Add(t, damage);
                        }
                        else
                        {
                            res.damageDealt[player][t] = res.damageDealt[player][t] + damage;
                        }
                    }
                    else if (logMessage.Substring(5, 14) == " You advanced " && logMessage.Contains("level", StringComparison.OrdinalIgnoreCase))
                    {
                        // advancement log message (You advanced from level x to level x + 1.)
                        if (logMessage[logMessage.Length - 1] == '.')
                        {
                            if (GlobalDataManager.AddLevelAdvance(logMessage))
                            {
                                res.newAdvances.Add(logMessage);
                            }
                        }
                    }
                    else
                    {
                        foreach (Event ev in StorageManager.eventIdMap.Values)
                        {
                            foreach (string evMessage in ev.eventMessages)
                            {
                                if (logMessage.Length == evMessage.Length + 6 && logMessage.Contains(evMessage.Trim(), StringComparison.OrdinalIgnoreCase))
                                {
                                    res.eventMessages.Add(new Tuple <Event, string>(ev, logMessage));
                                }
                            }
                        }
                    }
                }
            }

            return(chunksExist);
        }
예제 #16
0
        private static void SearchChunk(IEnumerable<string> chunk, ReadMemoryResults res, bool readChatMessages = true, bool readLogMessages = true)
        {
            List<int> stamps = TimestampManager.getLatestStamps(3, ignoreStamp);
            foreach (string it in chunk) {
                string logMessage = it;
                string t = logMessage.Substring(0, 5);
                int hour = int.Parse(logMessage.Substring(0, 2));
                int minute = int.Parse(logMessage.Substring(3, 2));
                if (!stamps.Contains(TimestampManager.getStamp(hour, minute))) continue; // the log message is not recent, so we skip parsing it

                if (FlashClient) {
                    // there is some inconsistency with log messages, certain log messages use "12:00: Message.", others use "12:00 Message"
                    // if there is a : after the timestamp we remove it
                    if (logMessage[5] == ':') {
                        logMessage = logMessage.Remove(5, 1);
                    }
                }

                logMessage = logMessage.Replace(" (active prey bonus)", "");

                string message = logMessage.Substring(6); // message without timestamp
                if (readLogMessages) {
                    if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " You see " && logMessage[logMessage.Length - 1] == '.') {
                        // the message contains "you see", so it's a look message
                        if (!res.lookMessages.ContainsKey(t)) res.lookMessages.Add(t, new List<string>());
                        if (!skipDuplicateCommands || !res.lookMessages[t].Contains(logMessage)) {
                            res.lookMessages[t].Add(logMessage);
                        }
                        continue;
                    } else if (message.Contains(':')) {
                        if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") { // loot drop message
                            if (!res.itemDrops.ContainsKey(t)) res.itemDrops.Add(t, new List<string>());
                            res.itemDrops[t].Add(logMessage);
                            continue;
                        }
                    } else if (logMessage.Length > 17 && logMessage.Substring(5, 12) == " You gained " && logMessage.EndsWith("experience points.")) {
                        // the message is an experience string, "You gained x experience."
                        try {
                            int experience = int.Parse(logMessage.Substring(17).Split(' ')[0]);
                            if (!res.exp.ContainsKey(t)) res.exp.Add(t, experience);
                            else res.exp[t] = res.exp[t] + experience;
                        } catch {
                        }
                        continue;
                    } else if (logMessage.Length == 19 && logMessage.Substring(5, 14) == " You are dead.") {
                        if (!res.deaths.ContainsKey(t))
                            res.deaths.Add(t, true);
                    } else if (logMessage.Length > 18) {
                        string[] split = message.Split(' ');
                        int index = split.IndexOf("hitpoints");
                        if (index > 0) {
                            int ind;
                            // damage log message (X loses Y hitpoints due to an attack by Z.)
                            int damage = 0;
                            if (!int.TryParse(split[index - 1], out damage)) {
                                continue;
                            }
                            string player;
                            if ((logMessage.Substring(logMessage.Length - 12) == "your attack.") || (logMessage.Substring(logMessage.Length - 21) == "your critical attack.")) {
                                // X lost Y hitpoints because of your attack.
                                // attacker is the player himself
                                player = "You";
                            } else if (split.Contains("by")) {
                                // X lost Y hitpoints because of an attack by Z.
                                // Z is the attacker => after the word "by"
                                player = "";
                                ind = split.IndexOf("by") + 1;
                                for (int i = ind; i < split.Length; i++) {
                                    player = (player == "" ? player : player + " ") + split[i];
                                }
                            } else {
                                continue;
                            }
                            string splitTerm;
                            if (split.Contains("loses")) {
                                splitTerm = "loses";
                            } else if (split.Contains("lose")) {
                                splitTerm = "lose";
                            } else {
                                continue;
                            }
                            ind = split.IndexOf(splitTerm);
                            string target = "";
                            for (int i = 0; i < ind; i++) {
                                target = (target == "" ? target : target + " ") + split[i];
                            }
                            if (!res.damageDealt.ContainsKey(player)) {
                                res.damageDealt.Add(player, new Dictionary<string, DamageEntry>());
                            }
                            DamageEntry damageEntry;
                            if (!res.damageDealt[player].ContainsKey(t)) {
                                damageEntry = new DamageEntry();
                                damageEntry.damage = damage;
                                damageEntry.targetDamage.Add(target, damage);
                                res.damageDealt[player].Add(t, damageEntry);
                            } else {
                                damageEntry = res.damageDealt[player][t];
                                damageEntry.damage += damage;
                                if (damageEntry.targetDamage.ContainsKey(target)) {
                                    damageEntry.targetDamage[target] += damage;
                                } else {
                                    damageEntry.targetDamage.Add(target, damage);
                                }
                            }
                            continue;
                        } else {
                            index = split.IndexOf("hitpoints.");
                            if (index > 0) {
                                // heal log message (X healed Y for Z hitpoints.)
                                int healing = 0;
                                if (!int.TryParse(split[index - 1], out healing)) {
                                    continue;
                                }

                                int forIndex = split.IndexOf("for");
                                if (forIndex <= 0) {
                                    continue;
                                }

                                string splitTerm;
                                if (split.Contains("heal")) {
                                    splitTerm = "heal";
                                } else if (split.Contains("healed")) {
                                    splitTerm = "healed";
                                } else {
                                    continue;
                                }
                                int healIndex = split.IndexOf(splitTerm);
                                if (healIndex >= forIndex) {
                                    continue;
                                }

                                string source = "";
                                for (int i = 0; i < healIndex; i++) {
                                    if (split[i] == "was" || split[i] == "were") break;
                                    if (split[i] == "by") continue;
                                    source = (source == "" ? source : source + " ") + split[i];
                                }
                                string target = "";
                                for (int i = healIndex + 1; i < forIndex; i++) {
                                    if (split[i] == "was" || split[i] == "were") break;
                                    if (split[i] == "by") continue;
                                    target = (target == "" ? target : target + " ") + split[i];
                                }
                                if (target == "yourself" || target == "itself" || target == "himself" || target == "herself") {
                                    target = source;
                                }

                                if (target.Length == 0 || source.Length == 0) {
                                    continue;
                                }

                                if (split.Contains("by")) {
                                    // X healed Y for Z. => X is the source and Y is the target (default)
                                    // X was healed by Y for Z. => X is the target and Y is the source, so swap source and target
                                    string temp = source;
                                    source = target;
                                    target = temp;
                                }

                                if (!res.healingDone.ContainsKey(source)) {
                                    res.healingDone.Add(source, new Dictionary<string, DamageEntry>());
                                }
                                DamageEntry healingEntry;
                                if (!res.healingDone[source].ContainsKey(t)) {
                                    healingEntry = new DamageEntry();
                                    healingEntry.damage = healing;
                                    healingEntry.targetDamage.Add(target, healing);
                                    res.healingDone[source].Add(t, healingEntry);
                                } else {
                                    healingEntry = res.healingDone[source][t];
                                    healingEntry.damage += healing;
                                    if (healingEntry.targetDamage.ContainsKey(target)) {
                                        healingEntry.targetDamage[target] += healing;
                                    } else {
                                        healingEntry.targetDamage.Add(target, healing);
                                    }
                                }
                            } else if (logMessage.Substring(5, 14) == " You advanced " && logMessage.Contains("level", StringComparison.OrdinalIgnoreCase)) {
                                // advancement log message (You advanced from level x to level x + 1.)
                                if (logMessage[logMessage.Length - 1] == '.') {
                                    if (GlobalDataManager.AddLevelAdvance(logMessage)) {
                                        res.newAdvances.Add(logMessage);
                                    }
                                    continue;
                                }
                            } else if (logMessage.Substring(5, 7) == " Using " && logMessage.Substring(logMessage.Length - 3, 3) == "...") {
                                // using log message (Using one of X items...)
                                var values = Parser.ParseUsingMessage(logMessage);
                                if (!res.usingMessages.ContainsKey(values.Item1)) res.usingMessages.Add(values.Item1, new Dictionary<string, HashSet<int>>());
                                if (!res.usingMessages[values.Item1].ContainsKey(t)) res.usingMessages[values.Item1].Add(t, new HashSet<int>());
                                res.usingMessages[values.Item1][t].Add(values.Item2);
                                continue;
                            } else if (logMessage.Length > 50 && logMessage.Substring(5, 45) == " Congratulations! You earned the achievement ") {
                                Achievement achievement = StorageManager.getAchievement(Parser.GetAchievement(logMessage));
                                if (achievement != null) {
                                    res.achievements.Add(new Tuple<Achievement, string>(achievement, logMessage));
                                } else {
                                    Console.WriteLine("Unrecognized achievement {0}.", Parser.GetAchievement(logMessage));
                                }
                            } else {
                                foreach (Event ev in StorageManager.eventIdMap.Values) {
                                    foreach (string evMessage in ev.eventMessages) {
                                        if (logMessage.Length == evMessage.Length + 6 && logMessage.Contains(evMessage.Trim(), StringComparison.OrdinalIgnoreCase)) {
                                            res.eventMessages.Add(new Tuple<Event, string>(ev, logMessage));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if (readChatMessages) {
                    if (message.Contains(':')) {
                        if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") { // loot drop message
                            continue;
                        } else { // if the message contains the ':' symbol but is not a loot drop message, it is a chat message, i.e. a command or url
                                 // we only split at most once, because the chat message can contain the ':' symbol as well and we don't want to discard that
                            string[] split = message.Split(new char[] { ':' }, 2);
                            string command = split[1];
                            // now get the player name, we have to discard the level that is between brackets
                            // players can also have spaces in their name, so we take that into account
                            string[] playersplit = split[0].Split(' ');
                            string player = "";
                            foreach (string str in playersplit) {
                                if (str.Contains('[')) break;
                                player = (player == "" ? player : player + " ") + str;
                            }
                            if (player == "http" || player == "https") continue; // I don't remember why we do this, possible http link in a log message? not sure
                            if (command.Contains('@')) {
                                // @ symbol symbolizes a command, so if there is an @ symbol, we treat the string as a command
                                if (!res.commands.ContainsKey(t)) res.commands.Add(t, new List<Tuple<string, string>>());
                                var tpl = new Tuple<string, string>(player, command);
                                if (!skipDuplicateCommands || !res.commands[t].Contains(tpl)) {
                                    res.commands[t].Add(tpl);
                                }
                            } else if (command.Contains("www") || command.Contains("http") || command.Contains(".com") || command.Contains(".net") || command.Contains(".tv") || command.Contains(".br")) {
                                // check if the command is an url, we aren't really smart about this, just check for a couple of common url-like things
                                if (!res.urls.ContainsKey(t)) res.urls.Add(t, new List<Tuple<string, string>>());
                                res.urls[t].Add(new Tuple<string, string>(player, command));
                            }
                        }
                    }
                }
            }
        }
예제 #17
0
        public static void ReadMemoryWhiteList(Process process, Dictionary <int, HashSet <long> > newWhitelistedAddresses, bool flashClient, ReadMemoryResults results)
        {
            HashSet <long> whitelist;

            if (!newWhitelistedAddresses.TryGetValue(process.Id, out whitelist))
            {
                return;
            }

            IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id);

            int bytesRead = 0;  // number of bytes read with ReadProcessMemory

            foreach (long addr in whitelist)
            {
                IntPtr proc_min_address = new IntPtr(addr);

                MEMORY_BASIC_INFORMATION mem_basic_info;
                VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28);

                if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT)
                {
                    if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize)
                    {
                        memoryBuffer = new byte[mem_basic_info.RegionSize];
                    }

                    // read everything in the buffer above
                    ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead);
                    // scan the memory for strings that start with timestamps and end with the null terminator ('\0')
                    IEnumerable <string> timestampLines;
                    if (!flashClient)
                    {
                        timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead);
                    }
                    else
                    {
                        timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead);
                    }

                    SearchChunk(timestampLines, results);
                }
            }
        }
예제 #18
0
        private ReadMemoryResults ReadMemory()
        {
            ReadMemoryResults results = null;
            SYSTEM_INFO sys_info = new SYSTEM_INFO();
            GetSystemInfo(out sys_info);

            IntPtr proc_min_address = sys_info.minimumApplicationAddress;
            IntPtr proc_max_address = sys_info.maximumApplicationAddress;

            long proc_min_address_l = (long)proc_min_address;
            long proc_max_address_l = (long)proc_max_address;
            Process process = GetTibiaProcess();
            if (process == null) {
                // Tibia process could not be found, wait for a bit and return
                Thread.Sleep(250);
                return null;
            }
            flashClient = TibiaClientName.ToLower().Contains("flash") || TibiaClientName.ToLower().Contains("chrome");
            IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id);
            MEMORY_BASIC_INFORMATION mem_basic_info = new MEMORY_BASIC_INFORMATION();
            int bytesRead = 0;  // number of bytes read with ReadProcessMemory
            int scanSpeed = SettingsManager.getSettingInt("ScanSpeed");
            try {
                results = new ReadMemoryResults();
                while (proc_min_address_l < proc_max_address_l) {
                    proc_min_address = new IntPtr(proc_min_address_l);
                    // 28 = sizeof(MEMORY_BASIC_INFORMATION)
                    VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28);

                    // check if this memory chunk is accessible
                    if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) {
                        if (!memorySegments.Contains(mem_basic_info.BaseAddress)) {
                            byte[] buffer = new byte[mem_basic_info.RegionSize];

                            // read everything in the buffer above
                            ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, buffer, mem_basic_info.RegionSize, ref bytesRead);
                            // scan the memory for strings that start with timestamps and end with the null terminator ('\0')
                            List<string> strings;
                            if (!flashClient) {
                                strings = FindTimestamps(buffer);
                            } else {
                                strings = FindTimestampsFlash(buffer);
                            }
                            if (strings.Count > 0) {
                                // if any timestamp strings were found, scan the chunk for any messages
                                SearchChunk(strings, results);
                            } else {
                                memorySegments.Add(mem_basic_info.BaseAddress);
                            }
                            // performance throttling sleep after every scan (depending on scanSpeed setting)
                            if (scanSpeed > 0) {
                                Thread.Sleep(scanSpeed);
                            }
                        }
                    }

                    // move to the next memory chunk
                    proc_min_address_l += mem_basic_info.RegionSize;
                }
            } catch {
                return null;
            }
            if (memorySegments.Count > 10) {
                memorySegments.RemoveRange(0, 10);
            } else {
                memorySegments.Clear();
            }
            process.Dispose();
            FinalCleanup(results);
            return results;
        }
예제 #19
0
        private void SearchChunk(List<string> chunk, ReadMemoryResults res)
        {
            List<int> stamps = getLatestStamps(3, ignoreStamp);
            for (int it = 0; it < chunk.Count; it++) {
                string logMessage = chunk[it];
                string t = logMessage.Substring(0, 5);
                int hour = int.Parse(logMessage.Substring(0, 2));
                int minute = int.Parse(logMessage.Substring(3, 2));
                if (!stamps.Contains(getStamp(hour, minute))) continue; // the log message is not recent, so we skip parsing it

                if (flashClient) {
                    // there is some inconsistency with log messages, certain log messages use "12:00: Message.", others use "12:00 Message"
                    // if there is a : after the timestamp we remove it
                    if (logMessage[5] == ':') {
                        logMessage = logMessage.Remove(5, 1);
                    }
                }
                string message = logMessage.Substring(6); // message without timestamp
                if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " You see ") {
                    // the message contains "you see", so it's a look message
                    if (!res.lookMessages.ContainsKey(t)) res.lookMessages.Add(t, new List<string>());
                    res.lookMessages[t].Add(logMessage);
                } else if (message.Contains(':')) {
                    if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") { // loot drop message
                        if (!res.itemDrops.ContainsKey(t)) res.itemDrops.Add(t, new List<string>());
                        res.itemDrops[t].Add(logMessage);
                    } else { // if the message contains the ':' symbol but is not a loot drop message, it is a chat message, i.e. a command or url
                             // we only split at most once, because the chat message can contain the ':' symbol as well and we don't want to discard that
                        string[] split = message.Split(new char[] { ':' }, 2);
                        string command = split[1];
                        // now get the player name, we have to discard the level that is between brackets
                        // players can also have spaces in their name, so we take that into account
                        string[] playersplit = split[0].Split(' ');
                        string player = "";
                        foreach (string str in playersplit) {
                            if (str.Contains('[')) break;
                            player = (player == "" ? player : player + " ") + str;
                        }
                        if (player == "http" || player == "https") continue; // I don't remember why we do this, possible http link in a log message? not sure
                        if (command.Contains('@')) {
                            // @ symbol symbolizes a command, so if there is an @ symbol, we treat the string as a command
                            if (!res.commands.ContainsKey(t)) res.commands.Add(t, new List<Tuple<string, string>>());
                            res.commands[t].Add(new Tuple<string, string>(player, command));
                        } else if (command.Contains("www") || command.Contains("http") || command.Contains(".com") || command.Contains(".net") || command.Contains(".tv") || command.Contains(".br")) {
                            // check if the command is an url, we aren't really smart about this, just check for a couple of common url-like things
                            if (!res.urls.ContainsKey(t)) res.urls.Add(t, new List<Tuple<string, string>>());
                            res.urls[t].Add(new Tuple<string, string>(player, command));
                        }
                    }
                } else if (logMessage.Length > 17 && logMessage.Substring(5, 12) == " You gained ") {
                    // the message is an experience string, "You gained x experience."
                    try {
                        int experience = int.Parse(logMessage.Substring(17).Split(' ')[0]);
                        if (!res.exp.ContainsKey(t)) res.exp.Add(t, experience);
                        else res.exp[t] = res.exp[t] + experience;
                    } catch {
                        continue;
                    }
                } else if (logMessage.Length == 19 && logMessage.Substring(5, 14) == " You are dead.") {
                    if (!res.deaths.ContainsKey(t))
                        res.deaths.Add(t, true);
                } else if (logMessage.Length > 18) {
                    string[] split = message.Split(' ');
                    if (split.Contains("hitpoints") && split.ToList().IndexOf("hitpoints") > 0) {
                        // damage log message (X loses Y hitpoints due to an attack by Z.)
                        int damage = 0;
                        if (!int.TryParse(split[split.ToList().IndexOf("hitpoints") - 1], out damage)) {
                            continue;
                        }
                        string player;
                        if (logMessage.Substring(logMessage.Length - 12) == "your attack.") {
                            // X lost Y hitpoints because of your attack.
                            // attacker is the player himself
                            player = "You";
                        } else {
                            // X lost Y hitpoints because of an attack by Z.
                            // Z is the attacker => after the word "by"
                            if (!split.Contains("by")) continue;
                            player = "";
                            int ind = split.ToList().IndexOf("by") + 1;
                            for (int i = ind; i < split.Length; i++) {
                                player = (player == "" ? player : player + " ") + split[i];
                            }
                        }
                        if (!res.damageDealt.ContainsKey(player)) res.damageDealt.Add(player, new Dictionary<string, int>());
                        if (!res.damageDealt[player].ContainsKey(t)) res.damageDealt[player].Add(t, damage);
                        else res.damageDealt[player][t] = res.damageDealt[player][t] + damage;
                    } else if (logMessage.Substring(5, 14) == " You advanced " && logMessage.ToLower().Contains("level")) {
                        // advancement log message (You advanced from level x to level x + 1.)
                        if (logMessage[logMessage.Length - 1] == '.' && !levelAdvances.Contains(logMessage)) {
                            res.newAdvances.Add(logMessage);
                            levelAdvances.Add(logMessage);
                        }
                    } else {
                        foreach (Event ev in eventIdMap.Values) {
                            foreach (string evMessage in ev.eventMessages) {
                                if (logMessage.Length == evMessage.Length + 6 && logMessage.ToLower().Contains(evMessage.ToLower().Trim())) {
                                    res.eventMessages.Add(new Tuple<Event, string>(ev, logMessage));
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #20
0
        public static ReadMemoryResults ReadMemory()
        {
            ReadMemoryResults results = null;
            SYSTEM_INFO sys_info;
            GetSystemInfo(out sys_info);

            IntPtr proc_min_address = sys_info.minimumApplicationAddress;
            IntPtr proc_max_address = sys_info.maximumApplicationAddress;

            long proc_min_address_l = (long)proc_min_address;
            long proc_max_address_l = (long)proc_max_address;
            Process[] processes = ProcessManager.GetTibiaProcesses();
            if (processes == null || processes.Length == 0) {
                return null;
            }

            results = new ReadMemoryResults();
            skipDuplicateCommands = (FlashClient || !SettingsManager.getSettingBool("ScanInternalTabStructure")) && SettingsManager.getSettingBool("SkipDuplicateCommands");
            Dictionary<int, HashSet<long>> newWhitelistedAddresses = null;
            Interlocked.Exchange(ref newWhitelistedAddresses, whiteListedAddresses);

            if (ProcessManager.TibiaClientType == "Tibia11") {
                ReadTibia11Tabs(results);
            } else {
                foreach (Process process in processes) {
                    if (!FlashClient && SettingsManager.getSettingBool("ScanInternalTabStructure")) {
                        ReadMemoryInternal(process, results);
                        UseInternalScan = true;
                    } else {
                        ReadMemoryWhiteList(process, newWhitelistedAddresses, FlashClient, results);
                        UseInternalScan = false;
                    }
                    process.Dispose();
                }
            }
            FinalCleanup(results);
            return results;
        }
예제 #21
0
파일: Parser.cs 프로젝트: villor/Tibialyzer
        public static ParseMemoryResults ParseLogResults(ReadMemoryResults res)
        {
            if (res == null) return null;
            ParseMemoryResults o = new ParseMemoryResults();
            // first we add the new parsed damage logs to the totalDamageResults
            o.newDamage = GlobalDataManager.UpdateDamageInformation(res.damageDealt);
            // now that we have updated the damage results, fill in the DPS meter, we use damage from the last 15 minutes for this
            List<string> times = TimestampManager.getLatestTimes(15);
            GlobalDataManager.GenerateDamageResults(o.damagePerSecond, times);

            // similar to damage, we keep a totalExperienceResults list
            // first update it with the new information
            int newExperience = GlobalDataManager.UpdateExperience(res.exp);

            // now compute the experience per hour
            // we use the same formula Tibia itself does so we get the same value
            // this formula is basically, take the experience in the last 15 minutes and multiply it by 4
            o.expPerHour = GlobalDataManager.GetTotalExperience(times).Item1;

            // Parse event messages
            foreach(Tuple<Event, string> newEvent in GlobalDataManager.UpdateEventInformation(res.eventMessages)) {
                o.newEventMessages.Add(newEvent);
            }

            // Update the look information
            foreach(string newLook in GlobalDataManager.UpdateLookInformation(res.lookMessages)) {
                o.newLooks.Add(newLook);
            }

            // Update death information
            o.death = GlobalDataManager.UpdateDeaths(res.deaths);

            // now parse any new commands given by users
            foreach(string newCommand in GlobalDataManager.UpdateCommands(res.commands)) {
                o.newCommands.Add(newCommand);
            }

            // check new urls
            GlobalDataManager.UpdateURLs(res.urls);

            Parser.ParseLootMessages(HuntManager.activeHunt, res.itemDrops, o.newItems, true, true);
            HuntManager.activeHunt.totalExp += newExperience;

            readWatch.Stop();
            if (newExperience == 0) {
                if (ticksSinceExperience < 120) {
                    ticksSinceExperience += readWatch.Elapsed.TotalSeconds;
                }
            } else {
                ticksSinceExperience = 0;
            }
            if (ticksSinceExperience < 120) {
                HuntManager.activeHunt.totalTime += readWatch.Elapsed.TotalSeconds;
            }
            readWatch.Restart();
            HuntManager.SaveHunts();
            return o;
        }
예제 #22
0
        public static ReadMemoryResults ReadMemory()
        {
            ReadMemoryResults results = null;
            SYSTEM_INFO       sys_info;

            GetSystemInfo(out sys_info);

            IntPtr proc_min_address = sys_info.minimumApplicationAddress;
            IntPtr proc_max_address = sys_info.maximumApplicationAddress;

            long proc_min_address_l = (long)proc_min_address;
            long proc_max_address_l = (long)proc_max_address;

            Process[] processes = ProcessManager.GetTibiaProcesses();
            if (processes == null || processes.Length == 0)
            {
                return(null);
            }

            results     = new ReadMemoryResults();
            flashClient = ProcessManager.IsFlashClient();
            Dictionary <int, HashSet <long> > newWhitelistedAddresses = null;

            Interlocked.Exchange(ref newWhitelistedAddresses, whiteListedAddresses);
            foreach (Process process in processes)
            {
                HashSet <long> whitelist;
                if (!newWhitelistedAddresses.TryGetValue(process.Id, out whitelist))
                {
                    continue;
                }

                IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id);

                int bytesRead = 0;  // number of bytes read with ReadProcessMemory
                foreach (long addr in whitelist)
                {
                    proc_min_address = new IntPtr(addr);

                    MEMORY_BASIC_INFORMATION mem_basic_info;
                    VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28);

                    if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT)
                    {
                        if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize)
                        {
                            memoryBuffer = new byte[mem_basic_info.RegionSize];
                        }

                        // read everything in the buffer above
                        ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead);
                        // scan the memory for strings that start with timestamps and end with the null terminator ('\0')
                        IEnumerable <string> timestampLines;
                        if (!flashClient)
                        {
                            timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead);
                        }
                        else
                        {
                            timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead);
                        }

                        SearchChunk(timestampLines, results);
                    }
                }

                process.Dispose();
            }
            FinalCleanup(results);
            return(results);
        }