예제 #1
0
        private static uint GetTagAddress(ProcessMemoryStream stream, int tagIndex)
        {
            // Read the tag count and validate the tag index
            var reader = new BinaryReader(stream);
            reader.BaseStream.Position = 0x22AB008;
            var maxIndex = reader.ReadInt32();
            if (tagIndex >= maxIndex)
                return 0;

            // Read the tag index table to get the index of the tag in the address table
            reader.BaseStream.Position = 0x22AAFFC;
            var tagIndexTableAddress = reader.ReadUInt32();
            if (tagIndexTableAddress == 0)
                return 0;
            reader.BaseStream.Position = tagIndexTableAddress + tagIndex * 4;
            var addressIndex = reader.ReadInt32();
            if (addressIndex < 0)
                return 0;

            // Read the tag's address in the address table
            reader.BaseStream.Position = 0x22AAFF8;
            var tagAddressTableAddress = reader.ReadUInt32();
            if (tagAddressTableAddress == 0)
                return 0;
            reader.BaseStream.Position = tagAddressTableAddress + addressIndex * 4;
            return reader.ReadUInt32();
        }
예제 #2
0
        public SecondGenMapPointerReader(ProcessMemoryStream stream, EngineDescription engineInfo, PokingInformation info)
        {
            _baseAddress           = (long)stream.BaseProcess.MainModule.BaseAddress;
            _mapHeaderAddress      = _baseAddress + info.HeaderAddress.Value;
            _mapMagicAddress       = _baseAddress + info.MagicAddress.Value;
            _mapSharedMagicAddress = _baseAddress + info.SharedMagicAddress.Value;

            GetLayoutConstants(engineInfo);

            var reader = new EndianReader(stream, BitConverter.IsLittleEndian ? Endian.LittleEndian : Endian.BigEndian);

            ReadMapPointers32(reader);
            ReadMapHeader(reader);
            ProcessMapHeader();
        }
예제 #3
0
        public override bool Execute(List<string> args)
        {
            if (args.Count != 1 && args.Count != 2)
                return false;
            int tagIndex;
            if (!int.TryParse(args[0], NumberStyles.HexNumber, null, out tagIndex) || tagIndex < 0)
                return false;

            // Get the process to open
            Process process;
            if (args.Count == 2)
            {
                // Get process by ID
                int processId;
                if (!int.TryParse(args[1], NumberStyles.HexNumber, null, out processId) || processId < 0)
                    return false;
                try
                {
                    process = Process.GetProcessById(processId);
                }
                catch (ArgumentException)
                {
                    Console.Error.WriteLine("Unable to find a process with an ID of 0x{0:X}", processId);
                    return true;
                }
            }
            else
            {
                // Find first eldorado process
                var processes = Process.GetProcessesByName("eldorado");
                if (processes.Length == 0)
                {
                    Console.Error.WriteLine("Unable to find any eldorado.exe processes.");
                    return true;
                }
                process = processes[0];
            }
            using (var stream = new ProcessMemoryStream(process))
            {
                var address = GetTagAddress(stream, tagIndex);
                if (address != 0)
                    Console.WriteLine("Tag 0x{0:X} is loaded at 0x{1:X8} in process 0x{2:X}.", tagIndex, address, process.Id);
                else
                    Console.Error.WriteLine("Tag 0x{0:X} is not loaded in process 0x{1:X}.", tagIndex, process.Id);
            }
            return true;
        }
예제 #4
0
        public override bool Execute(List<string> args)
        {
            if (args.Count != 1 && args.Count != 2)
                return false;
            int tagIndex;
            if (!int.TryParse(args[0], NumberStyles.HexNumber, null, out tagIndex) || tagIndex < 0)
                return false;

            // Get the process to open
            Process process;
            if (args.Count == 2)
            {
                // Get process by ID
                int processId;
                if (!int.TryParse(args[1], NumberStyles.HexNumber, null, out processId) || processId < 0)
                    return false;
                try
                {
                    process = Process.GetProcessById(processId);
                }
                catch (ArgumentException)
                {
                    Console.Error.WriteLine("Unable to find a process with an ID of 0x{0:X}", processId);
                    return true;
                }
            }
            else
            {
                // Find first eldorado process
                var processes = Process.GetProcessesByName("eldorado");
                if (processes.Length == 0)
                {
                    Console.Error.WriteLine("Unable to find any eldorado.exe processes.");
                    return true;
                }
                process = processes[0];
            }
            using (var stream = new ProcessMemoryStream(process))
            {
                var address = GetTagAddress(stream, tagIndex);
                if (address != 0)
                    Console.WriteLine("Tag 0x{0:X} is loaded at 0x{1:X8} in process 0x{2:X}.", tagIndex, address, process.Id);
                else
                    Console.Error.WriteLine("Tag 0x{0:X} is not loaded in process 0x{1:X}.", tagIndex, process.Id);
            }
            return true;
        }
예제 #5
0
        static void PatchClient(UserSettings settings, SuspendedProcess process, ClientVersion clientVersion, IPAddress serverIPAddress, int serverPort)
        {
            if (settings.ShouldRedirectClient && serverIPAddress == null)
            {
                throw new ArgumentNullException("serverIPAddress", "Server IP address must be specified when redirecting the client");
            }

            if (settings.ShouldRedirectClient && serverPort <= 0)
            {
                throw new ArgumentOutOfRangeException("Server port number must be greater than zero when redirecting the client");
            }

            using (var stream = new ProcessMemoryStream(process.ProcessId))
                using (var patcher = new RuntimePatcher(clientVersion, stream, leaveOpen: true))
                {
                    // Apply server hostname/port patch
                    if (settings.ShouldRedirectClient && clientVersion.ServerHostnamePatchAddress > 0 && clientVersion.ServerPortPatchAddress > 0)
                    {
                        Debug.WriteLine("Applying server redirect patch...");
                        patcher.ApplyServerHostnamePatch(serverIPAddress);
                        patcher.ApplyServerPortPatch(serverPort);
                    }

                    // Apply intro video patch
                    if (settings.ShouldSkipIntro && clientVersion.IntroVideoPatchAddress > 0)
                    {
                        Debug.WriteLine("Applying intro video patch...");
                        patcher.ApplySkipIntroVideoPatch();
                    }

                    // Apply multiple instances patch
                    if (settings.ShouldAllowMultipleInstances && clientVersion.MultipleInstancePatchAddress > 0)
                    {
                        Debug.WriteLine("Applying multiple instance patch...");
                        patcher.ApplyMultipleInstancesPatch();
                    }

                    // Apply hide walls patch
                    if (settings.ShouldHideWalls && clientVersion.HideWallsPatchAddress > 0)
                    {
                        Debug.WriteLine("Applying hide walls patch...");
                        patcher.ApplyHideWallsPatch();
                    }
                }
        }
예제 #6
0
        /// <summary>
        ///     Obtains a stream which can be used to read and write a cache file's meta in realtime.
        ///     The stream will be set up such that offsets in the stream correspond to meta pointers in the cache file.
        /// </summary>
        /// <param name="cacheFile">The cache file to get a stream for.</param>
        /// <returns>The stream if it was opened successfully, or null otherwise.</returns>
        public IStream GetMetaStream(ICacheFile cacheFile)
        {
            Process gameProcess = FindGameProcess();

            if (gameProcess == null)
            {
                return(null);
            }

            var gameMemory = new ProcessMemoryStream(gameProcess);
            var mapInfo    = new H2VistaMapPointerReader(gameMemory);

            long metaAddress;

            if (cacheFile.Type != CacheFileType.Shared)
            {
                metaAddress = mapInfo.CurrentMetaAddress;

                // The map isn't shared, so make sure the map names match
                if (mapInfo.MapName != cacheFile.InternalName)
                {
                    gameMemory.Close();
                    return(null);
                }
            }
            else
            {
                metaAddress = mapInfo.SharedMetaAddress;

                // Make sure the shared and current map pointers are different,
                // or that the current map is the shared map
                if (mapInfo.MapType != CacheFileType.Shared && mapInfo.CurrentMetaAddress == mapInfo.SharedMetaAddress)
                {
                    gameMemory.Close();
                    return(null);
                }
            }

            var metaStream = new OffsetStream(gameMemory, metaAddress - cacheFile.MetaArea.BasePointer);

            return(new EndianStream(metaStream, BitConverter.IsLittleEndian ? Endian.LittleEndian : Endian.BigEndian));
        }
예제 #7
0
        private static uint GetTagAddress(ProcessMemoryStream stream, int tagIndex)
        {
            // Read the tag count and validate the tag index
            var reader = new BinaryReader(stream);

            reader.BaseStream.Position = 0x22AB008;
            var maxIndex = reader.ReadInt32();

            if (tagIndex >= maxIndex)
            {
                return(0);
            }

            // Read the tag index table to get the index of the tag in the address table
            reader.BaseStream.Position = 0x22AAFFC;
            var tagIndexTableAddress = reader.ReadUInt32();

            if (tagIndexTableAddress == 0)
            {
                return(0);
            }
            reader.BaseStream.Position = tagIndexTableAddress + tagIndex * 4;
            var addressIndex = reader.ReadInt32();

            if (addressIndex < 0)
            {
                return(0);
            }

            // Read the tag's address in the address table
            reader.BaseStream.Position = 0x22AAFF8;
            var tagAddressTableAddress = reader.ReadUInt32();

            if (tagAddressTableAddress == 0)
            {
                return(0);
            }
            reader.BaseStream.Position = tagAddressTableAddress + addressIndex * 4;
            return(reader.ReadUInt32());
        }
예제 #8
0
        /// <summary>
        /// Obtains a stream which can be used to read and write a cache file's meta in realtime.
        /// The stream will be set up such that offsets in the stream correspond to meta pointers in the cache file.
        /// </summary>
        /// <param name="cacheFile">The cache file to get a stream for.</param>
        /// <param name="tag">The tag to get a stream for.</param>
        /// <returns>
        /// The stream if it was opened successfully, or null otherwise.
        /// </returns>
        public IStream GetMetaStream(ICacheFile cacheFile, ITag tag)
        {
            // Open a handle to the game process
            var gameProcess = FindGameProcess();

            if (gameProcess == null)
            {
                return(null);
            }
            var memoryStream = new ProcessMemoryStream(gameProcess);

            var exeBase = (uint)memoryStream.BaseProcess.MainModule.BaseAddress - 0x400000;

            var tagAddress = GetTagAddress(new EndianReader(memoryStream, Endian.LittleEndian), tag.Index.Index, exeBase);

            if (tagAddress == 0)
            {
                memoryStream.Close();
                return(null);
            }
            var offsetStream = new OffsetStream(memoryStream, tagAddress - tag.HeaderLocation.AsOffset());

            return(new EndianStream(offsetStream, Endian.LittleEndian));
        }
        public ThirdGenMapPointerReader(ProcessMemoryStream processStream, EngineDescription engineInfo, PokingInformation info)
        {
            _baseAddress = (long)processStream.BaseProcess.MainModule.BaseAddress;
            GetLayoutConstants(engineInfo);

            var reader = new EndianReader(processStream, BitConverter.IsLittleEndian ? Endian.LittleEndian : Endian.BigEndian);

            if (info.HeaderPointer.HasValue)
            {
                reader.SeekTo(_baseAddress + info.HeaderPointer.Value);
                long address = reader.ReadUInt32();
                _mapHeaderAddress = address + 0x8;
                _mapMagicAddress  = address + engineInfo.HeaderSize + info.MagicOffset.Value;
            }
            else
            {
                _mapHeaderAddress = _baseAddress + info.HeaderAddress.Value;
                _mapMagicAddress  = _baseAddress + info.MagicAddress.Value;
            }

            ReadMapPointers32(reader);
            ReadMapHeader(reader);
            ProcessMapHeader();
        }
 // Start is called before the first frame update
 void Start()
 {
     stream = new ProcessMemoryStream(executableName, ProcessMemoryStream.Mode.Read);
 }
예제 #11
0
        private void Launch()
        {
            _config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            string path = _config.AppSettings.Settings["Path"].Value;

            if (!File.Exists(path))
            {
                MessageBox.Show("Hybrasyl Launcher could not find Dark Ages located at: \r\n" + path, "File not found!", MessageBoxButton.OK, MessageBoxImage.Hand);
                return;
            }

            ProcessInformation information;
            StartupInfo        startupInfo = new StartupInfo();

            startupInfo.Size = Marshal.SizeOf(startupInfo);



            Kernel32.CreateProcess(path, null, IntPtr.Zero, IntPtr.Zero, false, ProcessCreationFlags.Suspended,
                                   IntPtr.Zero, null, ref startupInfo, out information);

            using (ProcessMemoryStream stream = new ProcessMemoryStream(information.ProcessId,
                                                                        ProcessAccess.VmWrite | ProcessAccess.VmRead | ProcessAccess.VmOperation))
            {
                if (_config.AppSettings.Settings["SkipIntro"].Value == "true")
                {
                    stream.Position = 0x42E625L; // intro
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                }

                #region Legacy IPAddress Code
                /*Legacy IP Address code. do not remove.*/
                //stream.Position = 0x4333C2L; // ip
                //IPAddress addr = Dns.GetHostAddresses(HostName)[0];
                //var serverBytes = addr.GetAddressBytes();
                //stream.WriteByte(0x6A);
                //stream.WriteByte(serverBytes[3]);
                //stream.WriteByte(0x6A);
                //stream.WriteByte(serverBytes[2]);
                //stream.WriteByte(0x6A);
                //stream.WriteByte(serverBytes[1]);
                //stream.WriteByte(0x6A);
                //stream.WriteByte(serverBytes[0]);

                //stream.Position = 0x4333F5L; // port
                //stream.WriteByte((byte)(Port % 256));
                //stream.WriteByte((byte)(Port / 256));
                /*End legacy IP Address code. do not remove.*/
                #endregion

                var    hostBytes = Encoding.UTF8.GetBytes(HostName);
                byte[] endBytes  = new byte[12];
                if (hostBytes.Length != 12)
                {
                    for (var i = 0; i < hostBytes.Length; i++)
                    {
                        endBytes[i] = hostBytes[i];
                    }
                }
                else
                {
                    endBytes = hostBytes;
                }

                stream.Position = 0x6707A8L;
                stream.Write(endBytes, 0, 12);

                if (_config.AppSettings.Settings["MultiInstance"].Value == "true")
                {
                    stream.Position = 0x57A7D9L; // multi-instance
                    stream.WriteByte(0xEB);
                }
                else
                {
                    LaunchEnabled = false;
                    OnPropertyChanged("LaunchEnabled");
                }
            }
            Kernel32.ResumeThread(information.ThreadHandle);
            var process = Process.GetProcessById(information.ProcessId);
            process.WaitForInputIdle();

            User32.SetWindowText(process.MainWindowHandle, "DarkAges : Hybrasyl");
        }
예제 #12
0
        public override void CreateWriter()
        {
            ProcessMemoryStream stream = new ProcessMemoryStream(name, ProcessMemoryStream.Mode.AllAccess);

            writer = new Writer(stream, Settings.s.IsLittleEndian);
        }
예제 #13
0
        public override object Execute(List <string> args)
        {
            if (args.Count > 1)
            {
                return(false);
            }

            Process process;

            if (args.Count == 1)
            {
                if (!int.TryParse(args[0], NumberStyles.HexNumber, null, out int processId) || processId < 0)
                {
                    return(false);
                }

            #if !DEBUG
                try
                {
            #endif
                process = Process.GetProcessById(processId);
            #if !DEBUG
            }
            catch (ArgumentException)
            {
                Console.Error.WriteLine("Unable to find a process with an ID of 0x{0:X}", processId);
                return(true);
            }
            #endif
            }
            else
            {
                var processes = Process.GetProcessesByName("eldorado");

                if (processes.Length == 0)
                {
                    Console.Error.WriteLine("Unable to find any eldorado.exe processes.");
                    return(true);
                }

                process = processes[0];
            }

            using (var processStream = new ProcessMemoryStream(process))
            {
                var address = GetTagAddress(processStream, Tag.Index);

                var runtimeContext = new RuntimeSerializationContext(CacheContext, processStream);
                processStream.Position = address + Tag.DefinitionOffset;

                var definition = CacheContext.Deserializer.Deserialize(runtimeContext, TagDefinition.Find(Tag.Group.Tag));
                CacheContext.Serializer.Serialize(runtimeContext, Value);

                if (address != 0)
                {
                    Console.WriteLine("Tag 0x{0:X} is loaded at 0x{1:X8} in process 0x{2:X}.", Tag.Index, address, process.Id);
                }
                else
                {
                    Console.Error.WriteLine("Tag 0x{0:X} is not loaded in process 0x{1:X}.", Tag.Index, process.Id);
                }
            }

            return(true);
        }
예제 #14
0
        public override object Execute(List <string> args)
        {
            if (args.Count < 1 || args.Count > 2)
            {
                return(false);
            }


            if (!int.TryParse(args[0], NumberStyles.HexNumber, null, out int tagIndex) || tagIndex < 0)
            {
                return(false);
            }

            Process process;

            if (args.Count == 2)
            {
                if (!int.TryParse(args[1], NumberStyles.HexNumber, null, out int processId) || processId < 0)
                {
                    return(false);
                }

                try
                {
                    process = Process.GetProcessById(processId);
                }
                catch (ArgumentException)
                {
                    Console.Error.WriteLine("Unable to find a process with an ID of 0x{0:X}", processId);
                    return(true);
                }
            }
            else
            {
                var processes = Process.GetProcessesByName("eldorado");

                if (processes.Length == 0)
                {
                    Console.Error.WriteLine("Unable to find any eldorado.exe processes.");
                    return(true);
                }

                process = processes[0];
            }

            using (var stream = new ProcessMemoryStream(process))
            {
                var exeBase = (uint)stream.BaseProcess.MainModule.BaseAddress - 0x400000;
                var address = GetTagAddress(stream, tagIndex, exeBase);

                if (address != 0)
                {
                    Console.WriteLine("Tag 0x{0:X} is loaded at 0x{1:X8} in process 0x{2:X}.", tagIndex, address, process.Id);
                }
                else
                {
                    Console.Error.WriteLine("Tag 0x{0:X} is not loaded in process 0x{1:X}.", tagIndex, process.Id);
                }
            }

            return(true);
        }
예제 #15
0
        private void Launch()
        {
            _config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            string path = _config.AppSettings.Settings["Path"].Value;

            if (!File.Exists(path))
            {
                MessageBox.Show("Hybrasyl Launcher could not find Dark Ages located at: \r\n" + path, "File not found!", MessageBoxButton.OK, MessageBoxImage.Hand);
                return;
            }

            ProcessInformation information;
            StartupInfo startupInfo = new StartupInfo();
            startupInfo.Size = Marshal.SizeOf(startupInfo);

            

            Kernel32.CreateProcess(path, null, IntPtr.Zero, IntPtr.Zero, false, ProcessCreationFlags.Suspended,
                IntPtr.Zero, null, ref startupInfo, out information);

            using (ProcessMemoryStream stream = new ProcessMemoryStream(information.ProcessId,
                ProcessAccess.VmWrite | ProcessAccess.VmRead | ProcessAccess.VmOperation))
            {
                

                if (_config.AppSettings.Settings["SkipIntro"].Value == "true")
                {
                    stream.Position = 0x42E625L; // intro
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                    stream.WriteByte(0x90);
                }

                #region Legacy IPAddress Code
                /*Legacy IP Address code. do not remove.*/
                //stream.Position = 0x4333C2L; // ip
                //IPAddress addr = Dns.GetHostAddresses(HostName)[0];
                //var serverBytes = addr.GetAddressBytes();
                //stream.WriteByte(0x6A);
                //stream.WriteByte(serverBytes[3]);
                //stream.WriteByte(0x6A);
                //stream.WriteByte(serverBytes[2]);
                //stream.WriteByte(0x6A);
                //stream.WriteByte(serverBytes[1]);
                //stream.WriteByte(0x6A);
                //stream.WriteByte(serverBytes[0]);

                //stream.Position = 0x4333F5L; // port
                //stream.WriteByte((byte)(Port % 256));
                //stream.WriteByte((byte)(Port / 256));
                /*End legacy IP Address code. do not remove.*/
                #endregion

                var hostBytes = Encoding.UTF8.GetBytes(HostName);
                byte[] endBytes = new byte[12];
                if (hostBytes.Length != 12)
                {
                    for (var i = 0; i < hostBytes.Length; i++)
                    {
                        endBytes[i] = hostBytes[i];
                    }
                }
                else
                {
                    endBytes = hostBytes;
                }

                stream.Position = 0x6707A8L;
                stream.Write(endBytes, 0, 12);

                if (_config.AppSettings.Settings["MultiInstance"].Value == "true")
                {
                    stream.Position = 0x57A7D9L; // multi-instance
                    stream.WriteByte(0xEB);
                }
                else
                {
                    LaunchEnabled = false;
                    OnPropertyChanged("LaunchEnabled");
                }
            }
            Kernel32.ResumeThread(information.ThreadHandle);
            var process = Process.GetProcessById(information.ProcessId);
            process.WaitForInputIdle();

            User32.SetWindowText(process.MainWindowHandle, "DarkAges : Hybrasyl");
        }
예제 #16
0
        public override object Execute(List <string> args)
        {
            if (args.Count > 1)
            {
                return(false);
            }


            Process process;

            if (args.Count == 1)
            {
                if (!int.TryParse(args[0], NumberStyles.HexNumber, null, out int processId) || processId < 0)
                {
                    return(false);
                }

            #if !DEBUG
                try
                {
            #endif
                process = Process.GetProcessById(processId);
            #if !DEBUG
            }
            catch (ArgumentException)
            {
                Console.Error.WriteLine("Unable to find a process with an ID of 0x{0:X}", processId);
                return(true);
            }
            #endif
            }
            else
            {
                var processes = Process.GetProcessesByName("eldorado");

                if (processes.Length == 0)
                {
                    Console.Error.WriteLine("Unable to find any eldorado.exe processes.");
                    return(true);
                }

                process = processes[0];
            }

            using (var processStream = new ProcessMemoryStream(process))
            {
                var address = GetTagAddress(processStream, Tag.Index);
                if (address != 0)
                {
                    var runtimeContext = new RuntimeSerializationContext(Cache, processStream, address, Tag.Offset, Tag.CalculateHeaderSize(), Tag.TotalSize);

                    //pause the process during poking to prevent race conditions
                    Stopwatch stopWatch = new Stopwatch();
                    stopWatch.Start();
                    process.Suspend();
                    Cache.Serializer.Serialize(runtimeContext, Value);
                    process.Resume();
                    stopWatch.Stop();

                    Console.WriteLine($"Poked tag at 0x{address.ToString("X8")} in {stopWatch.ElapsedMilliseconds / 1000.0f} seconds");
                }
                else
                {
                    Console.Error.WriteLine("Tag 0x{0:X} is not loaded in process 0x{1:X}.", Tag.Index, process.Id);
                }
            }

            return(true);
        }
예제 #17
0
 public RuntimeSerializationContext(HaloOnlineCacheContext cacheContext, ProcessMemoryStream processStream)
 {
     CacheContext  = cacheContext;
     ProcessStream = processStream;
 }
예제 #18
0
        /// <summary>
        ///     Obtains a stream which can be used to read and write a cache file's meta in realtime.
        ///     The stream will be set up such that offsets in the stream correspond to meta pointers in the cache file.
        /// </summary>
        /// <param name="cacheFile">The cache file to get a stream for.</param>
        /// <returns>The stream if it was opened successfully, or null otherwise.</returns>
        public IStream GetMetaStream(ICacheFile cacheFile)
        {
            if (string.IsNullOrEmpty(_buildInfo.GameExecutable))
            {
                throw new InvalidOperationException("No gameExecutable value found in Engines.xml for engine " + _buildInfo.Name + ".");
            }
            if (_buildInfo.Poking == null)
            {
                throw new InvalidOperationException("No poking definitions found in Engines.xml for engine " + _buildInfo.Name + ".");
            }

            Process gameProcess = FindGameProcess();

            if (gameProcess == null)
            {
                return(null);
            }

            string version = gameProcess.MainModule.FileVersionInfo.FileVersion;

            PokingInformation info = _buildInfo.Poking.RetrieveInformation(version);

            if (info == null)
            {
                throw new InvalidOperationException("Game version " + version + " does not have poking information defined in the Formats folder.");
            }

            if (!info.HeaderAddress.HasValue)
            {
                throw new NotImplementedException("Second Generation poking requires a HeaderAddress value.");
            }
            if (!info.MagicAddress.HasValue)
            {
                throw new NotImplementedException("Second Generation poking requires a MagicAddress value.");
            }
            if (!info.SharedMagicAddress.HasValue)
            {
                throw new NotImplementedException("Second Generation poking requires a SharedMagicAddress value.");
            }

            var gameMemory = new ProcessMemoryStream(gameProcess);
            var mapInfo    = new SecondGenMapPointerReader(gameMemory, _buildInfo, info);

            long metaAddress;

            if (cacheFile.Type != CacheFileType.Shared)
            {
                metaAddress = mapInfo.CurrentCacheAddress;

                // The map isn't shared, so make sure the map names match
                if (mapInfo.MapName != cacheFile.InternalName)
                {
                    gameMemory.Close();
                    return(null);
                }
            }
            else
            {
                metaAddress = mapInfo.SharedCacheAddress;

                // Make sure the shared and current map pointers are different,
                // or that the current map is the shared map
                if (mapInfo.MapType != CacheFileType.Shared && mapInfo.CurrentCacheAddress == mapInfo.SharedCacheAddress)
                {
                    gameMemory.Close();
                    return(null);
                }
            }

            var metaStream = new OffsetStream(gameMemory, metaAddress - cacheFile.MetaArea.BasePointer);

            return(new EndianStream(metaStream, BitConverter.IsLittleEndian ? Endian.LittleEndian : Endian.BigEndian));
        }