/[RomCheater]/trunk/RomCheater/Docking/FloatingMemorySearcher.cs
ViewVC logotype

Diff of /trunk/RomCheater/Docking/FloatingMemorySearcher.cs

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 407 by william, Thu Jun 21 15:59:29 2012 UTC revision 408 by william, Thu Jun 21 18:10:21 2012 UTC
# Line 1023  namespace RomCheater.Docking Line 1023  namespace RomCheater.Docking
1023                  Debug.WriteLine(ex.ToString());                  Debug.WriteLine(ex.ToString());
1024              }              }
1025          }          }
1026            private void search_provider_OnBytesRead(OnBytesReadEventArgs e)
1027            {
1028                SearchDataTypes sdt = SearchArgs.DataType;
1029                bool unsigned = SearchArgs.IsUnsignedDataType;
1030                int Last_Whole_Percent_Done = 0;
1031                uint CurrentIndex = e.CurrentIndex;
1032                if (e.UserState != null)
1033                {
1034                    SearchResultWriter writer = (e.UserState as SearchResultWriter);
1035                    if (writer == null)
1036                        throw new InvalidOperationException("writer cannot be null");
1037                    using (MemoryStream ms = new MemoryStream(e.Data))
1038                    {
1039                        using (BinaryReader br = new BinaryReader(ms))
1040                        {
1041                            while (br.BaseStream.Position < br.BaseStream.Length)
1042                            {
1043                                try
1044                                {
1045                                    switch (sdt)
1046                                    {
1047                                        case SearchDataTypes._8bits:
1048                                            if (unsigned) { writer.WriteResult<Byte>((uint)br.BaseStream.Position, br.ReadByte()); }
1049                                            else { writer.WriteResult<SByte>((uint)br.BaseStream.Position, br.ReadSByte()); } break;
1050                                        case SearchDataTypes._16bits:
1051                                            if (unsigned) { writer.WriteResult<UInt16>((uint)br.BaseStream.Position, br.ReadUInt16()); }
1052                                            else { writer.WriteResult<Int16>((uint)br.BaseStream.Position, br.ReadInt16()); } break;
1053                                        case SearchDataTypes._32bits:
1054                                            if (unsigned) { writer.WriteResult<UInt32>((uint)br.BaseStream.Position, br.ReadUInt32()); }
1055                                            else { writer.WriteResult<Int32>((uint)br.BaseStream.Position, br.ReadInt32()); } break;
1056                                        case SearchDataTypes._64bits:
1057                                            if (unsigned) { writer.WriteResult<UInt64>((uint)br.BaseStream.Position, br.ReadUInt64()); }
1058                                            else { writer.WriteResult<Int64>((uint)br.BaseStream.Position, br.ReadInt64()); } break;
1059                                    }
1060                                }
1061                                catch (EndOfStreamException) { }
1062                                double double_percent_done = 100.0 * (double)((double)CurrentIndex / (double)e.TotalCount);
1063                                int int_percent_done = (int)double_percent_done;
1064                                if (int_percent_done != Last_Whole_Percent_Done && (((double)Last_Whole_Percent_Done / 2.0) == (int)((double)Last_Whole_Percent_Done / 2.0)))
1065                                {
1066                                    if (int_percent_done <= 100)
1067                                    {
1068                                        resultsprogress.Value = int_percent_done;
1069                                        resultsprogress.Message = string.Format(" -> Reading Address: 0x{0:x8}", (CurrentIndex + MemoryRangeStart));
1070                                        Last_Whole_Percent_Done = int_percent_done;
1071                                    }
1072                                }
1073                                CurrentIndex++;
1074                            }
1075                        
1076                        }
1077                    }
1078                }
1079            }
1080          private void SearchWorkerThread_DoWork(object sender, DoWorkEventArgs e)          private void SearchWorkerThread_DoWork(object sender, DoWorkEventArgs e)
1081          {          {
1082              Stopwatch st = new Stopwatch();              Stopwatch st = new Stopwatch();
# Line 1042  namespace RomCheater.Docking Line 1095  namespace RomCheater.Docking
1095    
1096              bool unsigned = SearchArgs.IsUnsignedDataType;              bool unsigned = SearchArgs.IsUnsignedDataType;
1097              SearchDataTypes sdt = SearchArgs.DataType;              SearchDataTypes sdt = SearchArgs.DataType;
1098              byte[] buffered_mem = new byte[(MemoryRangeSize - MemoryRangeStart)]; // throws OutOfMemoryException if size is over 2G              //byte[] buffered_mem = new byte[(MemoryRangeSize - MemoryRangeStart)]; // throws OutOfMemoryException if size is over 2G
1099                logger.Debug.WriteLine(string.Format("Buffered Memory Size -> 0x{0:x8}", MemoryRangeSize - MemoryRangeStart));
1100                resultsprogress.Value = 0;
1101                resultsprogress.Message = string.Format("Search is Warming Up...Please Wait...");
1102    
1103                Stopwatch provider_st = new Stopwatch();
1104                provider_st.Start();
1105              using (GenericMemoryProvider provider = new GenericMemoryProvider((IAcceptsProcessAndConfig)this))              using (GenericMemoryProvider provider = new GenericMemoryProvider((IAcceptsProcessAndConfig)this))
1106              {              {
1107                  provider.OpenProvider();                  provider.OpenProvider();
1108                  int bytes_read = 0;                                int count = (int)(MemoryRangeSize - MemoryRangeStart) / (int)STEP_SIZE;
1109                  provider.ReadProcessMemoryAtOnce(MemoryRangeStart, (MemoryRangeSize - MemoryRangeStart), out bytes_read, out buffered_mem);                  SearchResultWriter writer = new SearchResultWriter(count);
1110                    provider.OnBytesRead += new BaseEventHandler<OnBytesReadEventArgs>(search_provider_OnBytesRead);
1111                    provider.ReadProcessMemoryAtOnce(MemoryRangeStart, (MemoryRangeSize - MemoryRangeStart), writer);
1112                  provider.CloseProvider();                  provider.CloseProvider();
1113                    writer.Dispose();
1114              }              }
1115              if (buffered_mem.Length == 0) { logger.Warn.WriteLine("Buffered Memory is Zero Length."); return; }              provider_st.Stop();
1116              using (MemoryStream ms = new MemoryStream(buffered_mem))              logger.Profiler.WriteLine("It took a total of {0} seconds for the memory provider to complete it's operation(s).", provider_st.Elapsed.TotalSeconds);
1117                //if (buffered_mem.Length == 0) { logger.Warn.WriteLine("Buffered Memory is Zero Length."); return; }            
1118                int Last_Whole_Percent_Done = 0;
1119    
1120                #region First Search
1121                //if (SearchArgs.IsFirstSearch)
1122                //{
1123                //    st_first_search.Start();
1124                //    //SearchArgs.Results.Clear();
1125                //    r_ms.BaseStream.Seek(0, SeekOrigin.Begin);
1126                //    int count = (int)buffered_mem.Length / (int)STEP_SIZE;
1127                //    #region using (SearchResultWriter writer = new SearchResultWriter(count))
1128                //    using (SearchResultWriter writer = new SearchResultWriter(count))
1129                //    {
1130                //        //List<ResultType<object>> results_list = new List<ResultType<object>>();
1131                //        //for (uint i = 0; i < buffered_mem.Length; i += STEP_SIZE)
1132                //        //{
1133                //        #region while (r_ms.BaseStream.Position < r_ms.BaseStream.Length)
1134                //        while (r_ms.BaseStream.Position < r_ms.BaseStream.Length)
1135                //        {
1136                //            //using (ResultType<object> _tmp_result = new ResultType<object>())
1137                //            //{
1138                //            try
1139                //            {
1140                //                switch (sdt)
1141                //                {
1142                //                    case SearchDataTypes._8bits:
1143                //                        if (unsigned) { writer.WriteResult<Byte>((uint)r_ms.BaseStream.Position, r_ms.ReadByte()); }
1144                //                        else { writer.WriteResult<SByte>((uint)r_ms.BaseStream.Position, r_ms.ReadSByte()); } break;
1145                //                    case SearchDataTypes._16bits:
1146                //                        if (unsigned) { writer.WriteResult<UInt16>((uint)r_ms.BaseStream.Position, r_ms.ReadUInt16()); }
1147                //                        else { writer.WriteResult<Int16>((uint)r_ms.BaseStream.Position, r_ms.ReadInt16()); } break;
1148                //                    case SearchDataTypes._32bits:
1149                //                        if (unsigned) { writer.WriteResult<UInt32>((uint)r_ms.BaseStream.Position, r_ms.ReadUInt32()); }
1150                //                        else { writer.WriteResult<Int32>((uint)r_ms.BaseStream.Position, r_ms.ReadInt32()); } break;
1151                //                    case SearchDataTypes._64bits:
1152                //                        if (unsigned) { writer.WriteResult<UInt64>((uint)r_ms.BaseStream.Position, r_ms.ReadUInt64()); }
1153                //                        else { writer.WriteResult<Int64>((uint)r_ms.BaseStream.Position, r_ms.ReadInt64()); } break;
1154                //                }
1155                //            }
1156                //            catch (EndOfStreamException)
1157                //            {
1158                //                //logger.VerboseError.WriteLine(ex.ToString());
1159                //                break;
1160                //            }
1161                //            //results_list.Add(_tmp_result);
1162                //            //SearchArgs.Results.Add(_tmp_result);
1163                //            double double_percent_done = 100.0 * (double)((double)r_ms.BaseStream.Position / (double)r_ms.BaseStream.Length);
1164                //            int int_percent_done = (int)double_percent_done;
1165                //            if (int_percent_done != Last_Whole_Percent_Done)
1166                //            {
1167                //                if (int_percent_done <= 100)
1168                //                {
1169                //                    resultsprogress.Value = int_percent_done;
1170                //                    resultsprogress.Message = string.Format(" -> Reading Address: 0x{0:x8}", (r_ms.BaseStream.Position + MemoryRangeStart));
1171                //                    Last_Whole_Percent_Done = int_percent_done;
1172                //                }
1173                //            }
1174                //        }
1175                //        if (SearchWorkerThread.CancellationPending == true)
1176                //        {
1177                //            e.Cancel = true;
1178                //            return;
1179                //        }
1180                //        #endregion
1181                //    }
1182                //    #endregion
1183                //    //}
1184                //    //SearchArgs.Results.AddRange(results_list);
1185                //    //results_list = null;
1186                //    resultsprogress.Value = 100;
1187                //    resultsprogress.Message = "";
1188                //    //Application.DoEvents();
1189                //    st_first_search.Stop();
1190                //    logger.Profiler.WriteLine("First search took a total of {0} seconds to complete.", st_first_search.Elapsed.TotalSeconds);
1191                //    Last_Whole_Percent_Done = 0;
1192                //}
1193                #endregion
1194    
1195                #region Subsequent Searches
1196                //r_ms.BaseStream.Seek(0, SeekOrigin.Begin);
1197    
1198    
1199                // hack to help with OutOfMemory Exceptions (OldValue and Equal compare will always add all found search results)
1200                bool NeedToCompare = true;
1201                if (SearchArgs.CompareValueType == CompareValueTypes.OldValue &&
1202                    SearchArgs.CompareType == SearchCompareTypes.Equal &&
1203                    SearchArgs.IsFirstSearch)
1204              {              {
1205                  using (BinaryReader r_ms = new BinaryReader(ms))                  NeedToCompare = false;
1206                    //second_tmp_Results = null; // Free Memory
1207                }
1208    
1209                if (NeedToCompare)
1210                {
1211                    if (SearchArgs.CompareType != SearchCompareTypes.Between && SearchArgs.CompareType != SearchCompareTypes.NotBetween)
1212                  {                  {
1213                      logger.Debug.WriteLine(string.Format("Buffered Memory Size -> 0x{0:x8}", buffered_mem.Length));                      #region Non-Range Searches
1214                      int Last_Whole_Percent_Done = 0;                      st_nonrange_search.Start();
1215                        //second_tmp_Results = new List<ResultType<object>>(SearchArgs.Results.Count * 1024);
1216                        ////second_tmp_Results.c
1217    
1218                      #region First Search                      using (SearchResultReader reader = new SearchResultReader())
                     if (SearchArgs.IsFirstSearch)  
1219                      {                      {
1220                          st_first_search.Start();                          for (int i = 0; i < reader.ResultCount; i += 1)
                         //SearchArgs.Results.Clear();  
                         r_ms.BaseStream.Seek(0, SeekOrigin.Begin);  
                         int count = (int)buffered_mem.Length / (int)STEP_SIZE;  
                         #region using (SearchResultWriter writer = new SearchResultWriter(count))  
                         using (SearchResultWriter writer = new SearchResultWriter(count))  
1221                          {                          {
1222                              //List<ResultType<object>> results_list = new List<ResultType<object>>();                              if (reader.ReadCurrentAddess)
1223                              //for (uint i = 0; i < buffered_mem.Length; i += STEP_SIZE)                              {
1224                              //{                                  if (!reader.ReadCurrentValue)
                             #region while (r_ms.BaseStream.Position < r_ms.BaseStream.Length)  
                             while (r_ms.BaseStream.Position < r_ms.BaseStream.Length)  
                             {  
                                 //using (ResultType<object> _tmp_result = new ResultType<object>())  
                                 //{  
                                 try  
                                 {  
                                     switch (sdt)  
                                     {  
                                         case SearchDataTypes._8bits:  
                                             if (unsigned) { writer.WriteResult<Byte>((uint)r_ms.BaseStream.Position, r_ms.ReadByte()); }  
                                             else { writer.WriteResult<SByte>((uint)r_ms.BaseStream.Position, r_ms.ReadSByte()); } break;  
                                         case SearchDataTypes._16bits:  
                                             if (unsigned) { writer.WriteResult<UInt16>((uint)r_ms.BaseStream.Position, r_ms.ReadUInt16()); }  
                                             else { writer.WriteResult<Int16>((uint)r_ms.BaseStream.Position, r_ms.ReadInt16()); } break;  
                                         case SearchDataTypes._32bits:  
                                             if (unsigned) { writer.WriteResult<UInt32>((uint)r_ms.BaseStream.Position, r_ms.ReadUInt32()); }  
                                             else { writer.WriteResult<Int32>((uint)r_ms.BaseStream.Position, r_ms.ReadInt32()); } break;  
                                         case SearchDataTypes._64bits:  
                                             if (unsigned) { writer.WriteResult<UInt64>((uint)r_ms.BaseStream.Position, r_ms.ReadUInt64()); }  
                                             else { writer.WriteResult<Int64>((uint)r_ms.BaseStream.Position, r_ms.ReadInt64()); } break;  
                                     }  
                                 }  
                                 catch (EndOfStreamException)  
                                 {  
                                     //logger.VerboseError.WriteLine(ex.ToString());  
                                     break;  
                                 }  
                                 //results_list.Add(_tmp_result);  
                                 //SearchArgs.Results.Add(_tmp_result);  
                                 double double_percent_done = 100.0 * (double)((double)r_ms.BaseStream.Position / (double)r_ms.BaseStream.Length);  
                                 int int_percent_done = (int)double_percent_done;  
                                 if (int_percent_done != Last_Whole_Percent_Done)  
1225                                  {                                  {
1226                                      if (int_percent_done <= 100)                                      switch (SearchArgs.DataType)
1227                                      {                                      {
1228                                          resultsprogress.Value = int_percent_done;                                          case SearchDataTypes._8bits: if (unsigned) { reader.CurrentResult<byte>(); } else { reader.CurrentResult<sbyte>(); } break;
1229                                          resultsprogress.Message = string.Format(" -> Reading Address: 0x{0:x8}", (r_ms.BaseStream.Position + MemoryRangeStart));                                          case SearchDataTypes._16bits: if (unsigned) { reader.CurrentResult<ushort>(); } else { reader.CurrentResult<short>(); } break;
1230                                          Last_Whole_Percent_Done = int_percent_done;                                          case SearchDataTypes._32bits: if (unsigned) { reader.CurrentResult<uint>(); } else { reader.CurrentResult<int>(); } break;
1231                                            case SearchDataTypes._64bits: if (unsigned) { reader.CurrentResult<ulong>(); } else { reader.CurrentResult<long>(); } break;
1232                                      }                                      }
1233                                  }                                  }
1234                              }                              }
1235                              if (SearchWorkerThread.CancellationPending == true)                              uint address = reader.CurrentAddress();
1236                                if (MemoryRangeStart > 0 && !SearchArgs.IsFirstSearch) { address = address - MemoryRangeStart; }
1237                                try
1238                              {                              {
1239                                  e.Cancel = true;                                  //r_ms.BaseStream.Seek(address, SeekOrigin.Begin);
                                 return;  
1240                              }                              }
1241                              #endregion                              catch (Exception)
                         }  
                         #endregion  
                           
                         //}  
                         //SearchArgs.Results.AddRange(results_list);  
                         //results_list = null;  
                         resultsprogress.Value = 100;  
                         resultsprogress.Message = "";  
                         //Application.DoEvents();  
                         st_first_search.Stop();  
                         logger.Profiler.WriteLine("First search took a total of {0} seconds to complete.", st_first_search.Elapsed.TotalSeconds);  
                         Last_Whole_Percent_Done = 0;  
                     }  
                     #endregion  
   
                     #region Subsequent Searches  
                     r_ms.BaseStream.Seek(0, SeekOrigin.Begin);  
   
   
                     // hack to help with OutOfMemory Exceptions (OldValue and Equal compare will always add all found search results)  
                     bool NeedToCompare = true;  
                     if (SearchArgs.CompareValueType == CompareValueTypes.OldValue &&  
                         SearchArgs.CompareType == SearchCompareTypes.Equal &&  
                         SearchArgs.IsFirstSearch)  
                     {  
                         NeedToCompare = false;  
                         //second_tmp_Results = null; // Free Memory  
                     }  
   
                     if (NeedToCompare)  
                     {  
                         if (SearchArgs.CompareType != SearchCompareTypes.Between && SearchArgs.CompareType != SearchCompareTypes.NotBetween)  
                         {  
                             #region Non-Range Searches  
                             st_nonrange_search.Start();  
                             //second_tmp_Results = new List<ResultType<object>>(SearchArgs.Results.Count * 1024);  
                             ////second_tmp_Results.c  
   
                             using (SearchResultReader reader = new SearchResultReader())  
1242                              {                              {
1243                                  for (int i = 0; i < reader.ResultCount; i += 1)                                  throw;
1244                                  {                              }
1245                                      if (reader.ReadCurrentAddess)                              switch (SearchArgs.DataType)
1246                                      {                              {
1247                                          if (!reader.ReadCurrentValue)                                  #region Comparer Support
1248                                    #region case SearchDataTypes._8bits:
1249                                    case SearchDataTypes._8bits:
1250                                        if (SearchArgs.IsUnsignedDataType)
1251                                        {
1252                                            byte lookup_value = 0;
1253                                            using (GenericMemoryProvider gmp = new GenericMemoryProvider((IAcceptsProcessAndConfig)this)) { gmp.OpenProvider(); gmp.ReadMemory(address, out lookup_value); gmp.CloseProvider(); }
1254                                            _8bit_unsigned_comparer_ comparer = new _8bit_unsigned_comparer_(SearchArgs, address);
1255                                            byte value = 0;
1256                                            if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)
1257                                            {
1258                                                value = reader.CurrentResult<byte>();
1259                                                comparer.Value = value;
1260                                            }
1261                                            else
1262                                            {
1263                                                value = Convert.ToByte(SearchArgs.CompareStartValue);
1264                                                comparer.Value = value;
1265                                            }
1266                                            if (comparer.Compare(lookup_value, value))
1267                                          {                                          {
1268                                              switch (SearchArgs.DataType)                                              ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1269                                              {                                              second_tmp_Results.Add(_tmp_result);
                                                 case SearchDataTypes._8bits: if (unsigned) { reader.CurrentResult<byte>(); } else { reader.CurrentResult<sbyte>(); } break;  
                                                 case SearchDataTypes._16bits: if (unsigned) { reader.CurrentResult<ushort>(); } else { reader.CurrentResult<short>(); } break;  
                                                 case SearchDataTypes._32bits: if (unsigned) { reader.CurrentResult<uint>(); } else { reader.CurrentResult<int>(); } break;  
                                                 case SearchDataTypes._64bits: if (unsigned) { reader.CurrentResult<ulong>(); } else { reader.CurrentResult<long>(); } break;  
                                             }  
1270                                          }                                          }
1271                                            comparer = null; // free memory
1272                                      }                                      }
1273                                      uint address = reader.CurrentAddress();                                      else
                                     if (MemoryRangeStart > 0 && !SearchArgs.IsFirstSearch) { address = address - MemoryRangeStart; }  
                                     try  
1274                                      {                                      {
1275                                          r_ms.BaseStream.Seek(address, SeekOrigin.Begin);                                          sbyte lookup_value = 0;
1276                                            using (GenericMemoryProvider gmp = new GenericMemoryProvider((IAcceptsProcessAndConfig)this)) { gmp.OpenProvider(); gmp.ReadMemory(address, out lookup_value); gmp.CloseProvider(); }
1277                                            _8bit_signed_comparer_ comparer = new _8bit_signed_comparer_(SearchArgs, address);
1278                                            sbyte value = 0;
1279                                            if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)
1280                                            {
1281                                                value = reader.CurrentResult<sbyte>();
1282                                            }
1283                                            else
1284                                            {
1285                                                value = Convert.ToSByte(SearchArgs.CompareStartValue);
1286                                            }
1287                                            if (comparer.Compare(lookup_value, value))
1288                                            {
1289                                                ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1290                                                second_tmp_Results.Add(_tmp_result);
1291                                            }
1292                                            comparer = null; // free memory
1293                                      }                                      }
1294                                      catch(Exception)                                      break;
1295                                      {                                  #endregion
1296                                          throw;                                  #region case SearchDataTypes._16bits:
1297                                    case SearchDataTypes._16bits:
1298                                        if (SearchArgs.IsUnsignedDataType)
1299                                        {
1300                                            ushort lookup_value = 0;
1301                                            using (GenericMemoryProvider gmp = new GenericMemoryProvider((IAcceptsProcessAndConfig)this)) { gmp.OpenProvider(); gmp.ReadMemory(address, out lookup_value); gmp.CloseProvider(); }
1302                                            _16bit_unsigned_comparer_ comparer = new _16bit_unsigned_comparer_(SearchArgs, address);
1303                                            ushort value = 0;
1304                                            if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)
1305                                            {
1306                                                value = reader.CurrentResult<ushort>();
1307                                                comparer.Value = value;
1308                                            }
1309                                            else
1310                                            {
1311                                                value = Convert.ToUInt16(SearchArgs.CompareStartValue);
1312                                                comparer.Value = value;
1313                                            }
1314                                            if (comparer.Compare(lookup_value, value))
1315                                            {
1316                                                ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1317                                                second_tmp_Results.Add(_tmp_result);
1318                                            }
1319                                            comparer = null; // free memory
1320                                      }                                      }
1321                                      switch (SearchArgs.DataType)                                      else
1322                                      {                                      {
1323                                          #region Comparer Support                                          short lookup_value = 0;
1324                                          #region case SearchDataTypes._8bits:                                          using (GenericMemoryProvider gmp = new GenericMemoryProvider((IAcceptsProcessAndConfig)this)) { gmp.OpenProvider(); gmp.ReadMemory(address, out lookup_value); gmp.CloseProvider(); }
1325                                          case SearchDataTypes._8bits:                                          _16bit_signed_comparer_ comparer = new _16bit_signed_comparer_(SearchArgs, address);
1326                                              if (SearchArgs.IsUnsignedDataType)                                          short value = 0;
1327                                              {                                          if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)
1328                                                  byte lookup_value = r_ms.ReadByte();                                          {
1329                                                  _8bit_unsigned_comparer_ comparer = new _8bit_unsigned_comparer_(SearchArgs, address);                                              value = reader.CurrentResult<short>();
1330                                                  byte value = 0;                                          }
1331                                                  if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)                                          else
1332                                                  {                                          {
1333                                                      value = reader.CurrentResult<byte>();                                              value = Convert.ToInt16(SearchArgs.CompareStartValue);
1334                                                      comparer.Value = value;                                          }
1335                                                  }                                          if (comparer.Compare(lookup_value, value))
1336                                                  else                                          {
1337                                                  {                                              ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1338                                                      value = Convert.ToByte(SearchArgs.CompareStartValue);                                              second_tmp_Results.Add(_tmp_result);
1339                                                      comparer.Value = value;                                          }
1340                                                  }                                          comparer = null; // free memory
                                                 if (comparer.Compare(lookup_value, value))  
                                                 {  
                                                     ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);  
                                                     second_tmp_Results.Add(_tmp_result);  
                                                 }  
                                                 comparer = null; // free memory  
                                             }  
                                             else  
                                             {  
                                                 sbyte lookup_value = r_ms.ReadSByte();  
                                                 _8bit_signed_comparer_ comparer = new _8bit_signed_comparer_(SearchArgs, address);  
                                                 sbyte value = 0;  
                                                 if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)  
                                                 {  
                                                     value = reader.CurrentResult<sbyte>();  
                                                 }  
                                                 else  
                                                 {  
                                                     value = Convert.ToSByte(SearchArgs.CompareStartValue);  
                                                 }  
                                                 if (comparer.Compare(lookup_value, value))  
                                                 {  
                                                     ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);  
                                                     second_tmp_Results.Add(_tmp_result);  
                                                 }  
                                                 comparer = null; // free memory  
                                             }  
                                             break;  
                                         #endregion  
                                         #region case SearchDataTypes._16bits:  
                                         case SearchDataTypes._16bits:  
                                             if (SearchArgs.IsUnsignedDataType)  
                                             {  
                                                 ushort lookup_value = r_ms.ReadUInt16();  
                                                 _16bit_unsigned_comparer_ comparer = new _16bit_unsigned_comparer_(SearchArgs, address);  
                                                 ushort value = 0;  
                                                 if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)  
                                                 {  
                                                     value = reader.CurrentResult<ushort>();  
                                                     comparer.Value = value;  
                                                 }  
                                                 else  
                                                 {  
                                                     value = Convert.ToUInt16(SearchArgs.CompareStartValue);  
                                                     comparer.Value = value;  
                                                 }  
                                                 if (comparer.Compare(lookup_value, value))  
                                                 {  
                                                     ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);  
                                                     second_tmp_Results.Add(_tmp_result);  
                                                 }  
                                                 comparer = null; // free memory  
                                             }  
                                             else  
                                             {  
                                                 short lookup_value = r_ms.ReadInt16();  
                                                 _16bit_signed_comparer_ comparer = new _16bit_signed_comparer_(SearchArgs, address);  
                                                 short value = 0;  
                                                 if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)  
                                                 {  
                                                     value = reader.CurrentResult<short>();  
                                                 }  
                                                 else  
                                                 {  
                                                     value = Convert.ToInt16(SearchArgs.CompareStartValue);  
                                                 }  
                                                 if (comparer.Compare(lookup_value, value))  
                                                 {  
                                                     ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);  
                                                     second_tmp_Results.Add(_tmp_result);  
                                                 }  
                                                 comparer = null; // free memory  
                                             }  
                                             break;  
                                         #endregion  
                                         #region case SearchDataTypes._32bits:  
                                         case SearchDataTypes._32bits:  
                                             if (SearchArgs.IsUnsignedDataType)  
                                             {  
                                                 uint lookup_value = r_ms.ReadUInt32();  
                                                 _32bit_unsigned_comparer_ comparer = new _32bit_unsigned_comparer_(SearchArgs, address);  
                                                 uint value = 0;  
                                                 if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)  
                                                 {  
                                                     value = reader.CurrentResult<uint>();  
                                                     comparer.Value = value;  
                                                 }  
                                                 else  
                                                 {  
                                                     value = Convert.ToUInt32(SearchArgs.CompareStartValue);  
                                                     comparer.Value = value;  
                                                 }  
                                                 if (comparer.Compare(lookup_value, value))  
                                                 {  
                                                     ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);  
                                                     second_tmp_Results.Add(_tmp_result);  
                                                 }  
                                                 comparer = null; // free memory  
                                             }  
                                             else  
                                             {  
                                                 int lookup_value = r_ms.ReadInt32();  
                                                 _32bit_signed_comparer_ comparer = new _32bit_signed_comparer_(SearchArgs, address);  
                                                 int value = 0;  
                                                 if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)  
                                                 {  
                                                     value = reader.CurrentResult<int>();  
                                                 }  
                                                 else  
                                                 {  
                                                     value = Convert.ToInt32(SearchArgs.CompareStartValue);  
                                                 }  
                                                 if (comparer.Compare(lookup_value, value))  
                                                 {  
                                                     ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);  
                                                     second_tmp_Results.Add(_tmp_result);  
                                                 }  
                                                 comparer = null; // free memory  
                                             }  
                                             break;  
                                         #endregion  
                                         #region case SearchDataTypes._64bits:  
                                         case SearchDataTypes._64bits:  
                                             if (SearchArgs.IsUnsignedDataType)  
                                             {  
                                                 ulong lookup_value = r_ms.ReadUInt64();  
                                                 _64bit_unsigned_comparer_ comparer = new _64bit_unsigned_comparer_(SearchArgs, address);  
                                                 ulong value = 0;  
                                                 if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)  
                                                 {  
                                                     value = reader.CurrentResult<ulong>();  
                                                     comparer.Value = value;  
                                                 }  
                                                 else  
                                                 {  
                                                     value = Convert.ToUInt64(SearchArgs.CompareStartValue);  
                                                     comparer.Value = value;  
                                                 }  
                                                 if (comparer.Compare(lookup_value, value))  
                                                 {  
                                                     ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);  
                                                     second_tmp_Results.Add(_tmp_result);  
                                                 }  
                                                 comparer = null; // free memory  
                                             }  
                                             else  
                                             {  
                                                 long lookup_value = r_ms.ReadInt64();  
                                                 _64bit_signed_comparer_ comparer = new _64bit_signed_comparer_(SearchArgs, address);  
                                                 long value = 0;  
                                                 if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)  
                                                 {  
                                                     value = reader.CurrentResult<long>();  
                                                 }  
                                                 else  
                                                 {  
                                                     value = Convert.ToInt64(SearchArgs.CompareStartValue);  
                                                 }  
                                                 if (comparer.Compare(lookup_value, value))  
                                                 {  
                                                     ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);  
                                                     second_tmp_Results.Add(_tmp_result);  
                                                 }  
                                                 comparer = null; // free memory  
                                             }  
                                             break;  
                                         #endregion  
                                         #endregion  
1341                                      }                                      }
1342                                        break;
1343                                      double double_percent_done = 100.0 * (double)((double)i / (double)reader.ResultCount);                                  #endregion
1344                                      int int_percent_done = (int)double_percent_done;                                  #region case SearchDataTypes._32bits:
1345                                      if (int_percent_done != Last_Whole_Percent_Done)                                  case SearchDataTypes._32bits:
1346                                      {                                      if (SearchArgs.IsUnsignedDataType)
1347                                          if (int_percent_done <= 100)                                      {
1348                                            uint lookup_value = 0;
1349                                            using (GenericMemoryProvider gmp = new GenericMemoryProvider((IAcceptsProcessAndConfig)this)) { gmp.OpenProvider(); gmp.ReadMemory(address, out lookup_value); gmp.CloseProvider(); }
1350                                            _32bit_unsigned_comparer_ comparer = new _32bit_unsigned_comparer_(SearchArgs, address);
1351                                            uint value = 0;
1352                                            if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)
1353                                          {                                          {
1354                                              resultsprogress.Value = int_percent_done;                                              value = reader.CurrentResult<uint>();
1355                                              resultsprogress.Message = string.Format(" -> Reading Address: 0x{0:x8}", i + MemoryRangeStart);                                              comparer.Value = value;
                                             Last_Whole_Percent_Done = int_percent_done;  
1356                                          }                                          }
1357                                            else
1358                                            {
1359                                                value = Convert.ToUInt32(SearchArgs.CompareStartValue);
1360                                                comparer.Value = value;
1361                                            }
1362                                            if (comparer.Compare(lookup_value, value))
1363                                            {
1364                                                ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1365                                                second_tmp_Results.Add(_tmp_result);
1366                                            }
1367                                            comparer = null; // free memory
1368                                      }                                      }
1369                                        else
                                 }  
                             }  
                             st_nonrange_search.Stop();  
                             logger.Profiler.WriteLine("Non-Ranged search took a total of {0} seconds to complete.", st_nonrange_search.Elapsed.TotalSeconds);  
                             Last_Whole_Percent_Done = 0;  
                             #endregion  
                         }  
                         #region Ranged Searches  
 #if !DONOT_HAVE_RANGED_SEARCH_SUPPORT  
                         if (SearchArgs.CompareType == SearchCompareTypes.Between || SearchArgs.CompareType == SearchCompareTypes.NotBetween)  
                         {  
                             st_ranged_search.Start();  
                             object start, end;  
   
                             start = SearchArgs.CompareStartValue;  
                             end = SearchArgs.CompareEndValue;  
                             using (SearchResultReader reader = new SearchResultReader())  
                             {  
                                 for (int i = 0; i < reader.ResultCount; i += 1)  
                                 {  
                                     uint address = reader.CurrentAddress();  
                                     if (MemoryRangeStart > 0 && !SearchArgs.IsFirstSearch) { address = address - MemoryRangeStart; }  
                                     r_ms.BaseStream.Seek(address, SeekOrigin.Begin);  
                                     if (SearchArgs.CompareType == SearchCompareTypes.Between)  
1370                                      {                                      {
1371                                          InRangeComparer comparer = new InRangeComparer(address, 0);                                          int lookup_value = 0;
1372                                          if (comparer.Compare(start, end, SearchArgs.DataType, SearchArgs.IsUnsignedDataType, r_ms))                                          using (GenericMemoryProvider gmp = new GenericMemoryProvider((IAcceptsProcessAndConfig)this)) { gmp.OpenProvider(); gmp.ReadMemory(address, out lookup_value); gmp.CloseProvider(); }
1373                                            _32bit_signed_comparer_ comparer = new _32bit_signed_comparer_(SearchArgs, address);
1374                                            int value = 0;
1375                                            if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)
1376                                            {
1377                                                value = reader.CurrentResult<int>();
1378                                            }
1379                                            else
1380                                            {
1381                                                value = Convert.ToInt32(SearchArgs.CompareStartValue);
1382                                            }
1383                                            if (comparer.Compare(lookup_value, value))
1384                                          {                                          {
1385                                              ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);                                              ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1386                                              second_tmp_Results.Add(_tmp_result);                                              second_tmp_Results.Add(_tmp_result);
1387                                          }                                          }
1388                                          comparer = null;                                          comparer = null; // free memory
1389                                      }                                      }
1390                                      else if (SearchArgs.CompareType == SearchCompareTypes.NotBetween)                                      break;
1391                                      {                                  #endregion
1392                                          NotInRangeComparer comparer = new NotInRangeComparer(address, 0);                                  #region case SearchDataTypes._64bits:
1393                                          if (comparer.Compare(start, end, SearchArgs.DataType, SearchArgs.IsUnsignedDataType, r_ms))                                  case SearchDataTypes._64bits:
1394                                        if (SearchArgs.IsUnsignedDataType)
1395                                        {
1396                                            ulong lookup_value = 0;
1397                                            using (GenericMemoryProvider gmp = new GenericMemoryProvider((IAcceptsProcessAndConfig)this)) { gmp.OpenProvider(); gmp.ReadMemory(address, out lookup_value); gmp.CloseProvider(); }
1398                                            _64bit_unsigned_comparer_ comparer = new _64bit_unsigned_comparer_(SearchArgs, address);
1399                                            ulong value = 0;
1400                                            if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)
1401                                            {
1402                                                value = reader.CurrentResult<ulong>();
1403                                                comparer.Value = value;
1404                                            }
1405                                            else
1406                                            {
1407                                                value = Convert.ToUInt64(SearchArgs.CompareStartValue);
1408                                                comparer.Value = value;
1409                                            }
1410                                            if (comparer.Compare(lookup_value, value))
1411                                          {                                          {
1412                                              ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);                                              ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1413                                              second_tmp_Results.Add(_tmp_result);                                              second_tmp_Results.Add(_tmp_result);
1414                                          }                                          }
1415                                          comparer = null;                                          comparer = null; // free memory
1416                                      }                                      }
1417                                      else                                      else
1418                                      {                                      {
1419                                          throw new InvalidOperationException("Encounted unkown range search type: " + SearchArgs.CompareType);                                          long lookup_value = 0;
1420                                      }                                          using (GenericMemoryProvider gmp = new GenericMemoryProvider((IAcceptsProcessAndConfig)this)) { gmp.OpenProvider(); gmp.ReadMemory(address, out lookup_value); gmp.CloseProvider(); }
1421                                      double double_percent_done = 100.0 * (double)((double)i / (double)reader.ResultCount);                                          _64bit_signed_comparer_ comparer = new _64bit_signed_comparer_(SearchArgs, address);
1422                                      int int_percent_done = (int)double_percent_done;                                          long value = 0;
1423                                      if (int_percent_done != Last_Whole_Percent_Done)                                          if (SearchArgs.CompareValueType == CompareValueTypes.OldValue)
1424                                      {                                          {
1425                                          if (int_percent_done <= 100)                                              value = reader.CurrentResult<long>();
1426                                            }
1427                                            else
1428                                            {
1429                                                value = Convert.ToInt64(SearchArgs.CompareStartValue);
1430                                            }
1431                                            if (comparer.Compare(lookup_value, value))
1432                                          {                                          {
1433                                              resultsprogress.Value = int_percent_done;                                              ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1434                                              resultsprogress.Message = string.Format(" -> Reading Address: 0x{0:x8}", i + MemoryRangeStart);                                              second_tmp_Results.Add(_tmp_result);
                                             Last_Whole_Percent_Done = int_percent_done;  
1435                                          }                                          }
1436                                            comparer = null; // free memory
1437                                      }                                      }
1438                                        break;
1439                                    #endregion
1440                                    #endregion
1441                                }
1442    
1443                                double double_percent_done = 100.0 * (double)((double)i / (double)reader.ResultCount);
1444                                int int_percent_done = (int)double_percent_done;
1445                                if (int_percent_done != Last_Whole_Percent_Done && i % 100000 == 0)
1446                                {
1447                                    if (int_percent_done <= 100)
1448                                    {
1449                                        resultsprogress.Value = int_percent_done;
1450                                        resultsprogress.Message = string.Format(" -> Reading Address: 0x{0:x8}", i + MemoryRangeStart);
1451                                        Last_Whole_Percent_Done = int_percent_done;
1452                                  }                                  }
1453                              }                              }
                             st_ranged_search.Stop();  
                             logger.Profiler.WriteLine("Ranged search took a total of {0} seconds to complete.", st_ranged_search.Elapsed.TotalSeconds);  
                         }  
 #endif  
                         #endregion  
1454    
1455                            }
1456                      }                      }
1457                        st_nonrange_search.Stop();
1458                        logger.Profiler.WriteLine("Non-Ranged search took a total of {0} seconds to complete.", st_nonrange_search.Elapsed.TotalSeconds);
1459                        Last_Whole_Percent_Done = 0;
1460                      #endregion                      #endregion
1461                      // leave SearchArgs.Results alone, if false                  }
1462                      if (NeedToCompare)                  #region Ranged Searches
1463    #if !DONOT_HAVE_RANGED_SEARCH_SUPPORT
1464                    if (SearchArgs.CompareType == SearchCompareTypes.Between || SearchArgs.CompareType == SearchCompareTypes.NotBetween)
1465                    {
1466                        st_ranged_search.Start();
1467                        object start, end;
1468    
1469                        start = SearchArgs.CompareStartValue;
1470                        end = SearchArgs.CompareEndValue;
1471                        using (SearchResultReader reader = new SearchResultReader())
1472                      {                      {
1473                          // fix addresses when memory start is not zero                          for (int i = 0; i < reader.ResultCount; i += 1)
                         if (MemoryRangeStart > 0 && SearchArgs.IsFirstSearch) { for (int i = 0; i < second_tmp_Results.Count; i++) { second_tmp_Results[i].Address = second_tmp_Results[i].Address + MemoryRangeStart; } }  
                         using (SearchResultWriter writer = new SearchResultWriter(second_tmp_Results.Count))  
1474                          {                          {
1475                              for (int i = 0; i < second_tmp_Results.Count; i++)                              uint address = reader.CurrentAddress();
1476                                if (MemoryRangeStart > 0 && !SearchArgs.IsFirstSearch) { address = address - MemoryRangeStart; }
1477                                //r_ms.BaseStream.Seek(address, SeekOrigin.Begin);
1478                                if (SearchArgs.CompareType == SearchCompareTypes.Between)
1479                              {                              {
1480                                  switch (sdt)                                  InRangeComparer comparer = new InRangeComparer(address, 0);
1481                                    if (comparer.Compare(start, end, SearchArgs.DataType, SearchArgs.IsUnsignedDataType, (IAcceptsProcessAndConfig)this))
1482                                  {                                  {
1483                                      case SearchDataTypes._8bits:                                      ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1484                                          if (unsigned) { writer.WriteResult<Byte>(second_tmp_Results[i].Address, Convert.ToByte(second_tmp_Results[i].Value)); }                                      second_tmp_Results.Add(_tmp_result);
                                         else { writer.WriteResult<SByte>(second_tmp_Results[i].Address, Convert.ToSByte(second_tmp_Results[i].Value)); } break;  
                                     case SearchDataTypes._16bits:  
                                         if (unsigned) { writer.WriteResult<UInt16>(second_tmp_Results[i].Address, Convert.ToUInt16(second_tmp_Results[i].Value)); }  
                                         else { writer.WriteResult<Int16>(second_tmp_Results[i].Address, Convert.ToInt16(second_tmp_Results[i].Value)); } break;  
                                     case SearchDataTypes._32bits:  
                                         if (unsigned) { writer.WriteResult<UInt32>(second_tmp_Results[i].Address, Convert.ToUInt32(second_tmp_Results[i].Value)); }  
                                         else { writer.WriteResult<Int32>(second_tmp_Results[i].Address, Convert.ToInt32(second_tmp_Results[i].Value)); } break;  
                                     case SearchDataTypes._64bits:  
                                         if (unsigned) { writer.WriteResult<UInt64>(second_tmp_Results[i].Address, Convert.ToUInt64(second_tmp_Results[i].Value)); }  
                                         else { writer.WriteResult<Int64>(second_tmp_Results[i].Address, Convert.ToInt64(second_tmp_Results[i].Value)); } break;  
1485                                  }                                  }
1486                                    comparer = null;
1487                              }                              }
1488                          }                                                    else if (SearchArgs.CompareType == SearchCompareTypes.NotBetween)
1489                          second_tmp_Results = null; // free memory                                                    {
1490                                    NotInRangeComparer comparer = new NotInRangeComparer(address, 0);
1491                                    if (comparer.Compare(start, end, SearchArgs.DataType, SearchArgs.IsUnsignedDataType, (IAcceptsProcessAndConfig)this))
1492                                    {
1493                                        ResultType<object> _tmp_result = new ResultType<object>(comparer.Address, comparer.Value);
1494                                        second_tmp_Results.Add(_tmp_result);
1495                                    }
1496                                    comparer = null;
1497                                }
1498                                else
1499                                {
1500                                    throw new InvalidOperationException("Encounted unkown range search type: " + SearchArgs.CompareType);
1501                                }
1502                                double double_percent_done = 100.0 * (double)((double)i / (double)reader.ResultCount);
1503                                int int_percent_done = (int)double_percent_done;
1504                                if (int_percent_done != Last_Whole_Percent_Done)
1505                                {
1506                                    if (int_percent_done <= 100)
1507                                    {
1508                                        resultsprogress.Value = int_percent_done;
1509                                        resultsprogress.Message = string.Format(" -> Reading Address: 0x{0:x8}", i + MemoryRangeStart);
1510                                        Last_Whole_Percent_Done = int_percent_done;
1511                                    }
1512                                }
1513                            }
1514                      }                      }
1515                        st_ranged_search.Stop();
1516                        logger.Profiler.WriteLine("Ranged search took a total of {0} seconds to complete.", st_ranged_search.Elapsed.TotalSeconds);
1517                    }
1518    #endif
1519                    #endregion
1520    
1521                      r_ms.Close();              }
1522                #endregion
1523                // leave SearchArgs.Results alone, if false
1524                if (NeedToCompare)
1525                {
1526                    // fix addresses when memory start is not zero
1527                    if (MemoryRangeStart > 0 && SearchArgs.IsFirstSearch) { for (int i = 0; i < second_tmp_Results.Count; i++) { second_tmp_Results[i].Address = second_tmp_Results[i].Address + MemoryRangeStart; } }
1528                    using (SearchResultWriter writer = new SearchResultWriter(second_tmp_Results.Count))
1529                    {
1530                        for (int i = 0; i < second_tmp_Results.Count; i++)
1531                        {
1532                            switch (sdt)
1533                            {
1534                                case SearchDataTypes._8bits:
1535                                    if (unsigned) { writer.WriteResult<Byte>(second_tmp_Results[i].Address, Convert.ToByte(second_tmp_Results[i].Value)); }
1536                                    else { writer.WriteResult<SByte>(second_tmp_Results[i].Address, Convert.ToSByte(second_tmp_Results[i].Value)); } break;
1537                                case SearchDataTypes._16bits:
1538                                    if (unsigned) { writer.WriteResult<UInt16>(second_tmp_Results[i].Address, Convert.ToUInt16(second_tmp_Results[i].Value)); }
1539                                    else { writer.WriteResult<Int16>(second_tmp_Results[i].Address, Convert.ToInt16(second_tmp_Results[i].Value)); } break;
1540                                case SearchDataTypes._32bits:
1541                                    if (unsigned) { writer.WriteResult<UInt32>(second_tmp_Results[i].Address, Convert.ToUInt32(second_tmp_Results[i].Value)); }
1542                                    else { writer.WriteResult<Int32>(second_tmp_Results[i].Address, Convert.ToInt32(second_tmp_Results[i].Value)); } break;
1543                                case SearchDataTypes._64bits:
1544                                    if (unsigned) { writer.WriteResult<UInt64>(second_tmp_Results[i].Address, Convert.ToUInt64(second_tmp_Results[i].Value)); }
1545                                    else { writer.WriteResult<Int64>(second_tmp_Results[i].Address, Convert.ToInt64(second_tmp_Results[i].Value)); } break;
1546                            }
1547                        }
1548                  }                  }
1549                    second_tmp_Results = null; // free memory                      
1550              }              }
1551          }          }
1552    
1553          
1554    
1555          private void SearchWorkerThread_ProgressChanged(object sender, ProgressChangedEventArgs e)          private void SearchWorkerThread_ProgressChanged(object sender, ProgressChangedEventArgs e)
1556          {          {
1557              //if (SearchArgs.ProgressLogger != null)              //if (SearchArgs.ProgressLogger != null)

Legend:
Removed from v.407  
changed lines
  Added in v.408

  ViewVC Help
Powered by ViewVC 1.1.22