private ListOption LoadListOption(int optionIndex, int itemIndex)
        {
            var listOption = new ListOption
            {
                Name        = NativeMethods.GetOptionListItemName(optionIndex, itemIndex),
                Description = NativeMethods.GetOptionListItemDesc(optionIndex, itemIndex),
                Key         = NativeMethods.GetOptionListItemKey(optionIndex, itemIndex)
            };

            TraceErrors();
            return(listOption);
        }
 private ListOption LoadListOption(int optionIndex, int itemIndex)
 {
     var listOption = new ListOption
     {
         Name = NativeMethods.GetOptionListItemName(optionIndex, itemIndex),
         Description = NativeMethods.GetOptionListItemDesc(optionIndex, itemIndex),
         Key = NativeMethods.GetOptionListItemKey(optionIndex, itemIndex)
     };
     TraceErrors();
     return listOption;
 }
        /// <summary>
        /// Read options from EngineOptions.lua or ModOptions.lua
        /// </summary>
        public static void ReadOptionsTable(String filePath, ref Mod modInfo)
        {
            if (!File.Exists(filePath)) 
            {
                return;
            }
            
            var allOptions = new List<Option>();
            using (FileStream fileStream = File.OpenRead(filePath))
            using (var stream = new StreamReader(fileStream))
            {
                var allText = stream.ReadToEnd();
                int offset =0;
                var config = new TableReaderConfig();
                var table = TableReader.ParseTable(config,0,allText,filePath,out offset);
                
                foreach (var kvp in table)
                {
                    var anOption = new Option();
                    bool isBoolOption = false;
                    foreach(var kvp2 in (kvp.Value as Dictionary<String,Object>))
                    {
                        var value = (kvp2.Value as String);
                        float numbers;
                        //uncomment to take a peek on what it read!: 
                        //System.Diagnostics.Trace.TraceWarning("key: " + kvp2.Key + " value:" + value);
                        switch(kvp2.Key)
                        {
                            case "key":
                                anOption.Key = value;
                                break;
                            case "name":
                                anOption.Name = value;
                                break;
                            case "desc":
                                anOption.Description = value;
                                break;
                            case "type":
                                switch(value)
                                {
                                    case "bool":
                                        anOption.Type = OptionType.Bool;
                                        isBoolOption = true;
                                        break;
                                    case "list":
                                        anOption.Type = OptionType.List;
                                        break;
                                    case "number":
                                        anOption.Type = OptionType.Number;
                                        break;
                                    case "string":
                                        anOption.Type = OptionType.String;
                                        break;
                                    case "section":
                                        anOption.Type = OptionType.Section;
                                        continue;
                                    default:
                                        anOption.Type = OptionType.Undefined;
                                        break;
                                }
                                break;
                            case "def":
                                anOption.Default = value;
                                if (isBoolOption)
                                {
                                    if (value=="false")
                                        anOption.Default = "0";
                                    else
                                        anOption.Default = "1";
                                }
                                break;
                            case "min":
                                float.TryParse(value,NumberStyles.Float,CultureInfo.InvariantCulture,out numbers);
                                anOption.Min = numbers;
                                break;
                            case "max":
                                float.TryParse(value,NumberStyles.Float,CultureInfo.InvariantCulture,out numbers);
                                anOption.Max = numbers;
                                break;
                            case "step":
                                float.TryParse(value,NumberStyles.Float,CultureInfo.InvariantCulture,out numbers);
                                anOption.Step = numbers;
                                break;
                            case "maxlen":
                                float.TryParse(value,NumberStyles.Float,CultureInfo.InvariantCulture,out numbers);
                                anOption.StrMaxLen = numbers;
                                break;
                            case "items":
                                var listOptions = new List<ListOption>();
                                foreach(var kvp3 in (kvp2.Value as Dictionary<String,Object>))
                                {
                                    var listOption = new ListOption();
                                    foreach(var kvp4 in (kvp3.Value as Dictionary<String,Object>))
                                    {
                                        var value2 = (kvp4.Value as String);
                                        switch(kvp4.Key)
                                        {
                                            case "key":
                                                listOption.Key = value2;
                                                break;
                                            case "name":
                                                listOption.Name = value2;
                                                break;
                                            case "desc":
                                                listOption.Description = value2;
                                                break;
                                        }
                                    }

                                    if (listOption.Key!=null)
                                        listOptions.Add(listOption);
                                }
                                anOption.ListOptions = listOptions;
                                break;
                            case "scope":
                                anOption.Scope = value;
                                break;
                            case "section":
                                anOption.Section = value;
                                break;
                        }     
                    }

                    if (anOption.Key!=null)
                        allOptions.Add(anOption);   
                }
            }
            if (modInfo.Options!=null)
                modInfo.Options = modInfo.Options.ToList().Concat(allOptions).ToArray();
            else 
                modInfo.Options = allOptions.ToArray();
        }
        public IEnumerable <Ai> GetAis()
        {
            for (var i = 0; i < NativeMethods.GetSkirmishAICount(); i++)
            {
                yield return new Ai {
                           Info = GetAiInfo(i).ToArray(), Options = GetAiOptions(i).ToArray()
                }
            }
            ;
        }

        string GetArchivePath(string archiveName)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            var result = NativeMethods.GetArchivePath(archiveName);

            if (result == null)
            {
                throw new UnitSyncException(NativeMethods.GetNextError());
            }
            return(result);
        }

        /// <summary>
        /// Call AddAllArchives before this
        /// </summary>
        IEnumerable <string> GetFilesInVfsDirectory(string folder, string pattern)
        {
            var searchHandle = NativeMethods.InitDirListVFS(folder, pattern, null);

            return(CompleteFindFilesInVfs(searchHandle));
        }

        unsafe Bitmap GetInfoMap(string mapName, string name, int bytesPerPixel)
        {
            var width  = 0;
            var height = 0;

            if (!NativeMethods.GetInfoMapSize(mapName, name, ref width, ref height))
            {
                System.Diagnostics.Trace.TraceInformation("GetInfoMapSize failed");                                                                                 //ignore negative return
            }
            var infoMapData   = new byte[width * height];
            var infoMapHandle = GCHandle.Alloc(infoMapData, GCHandleType.Pinned);

            try
            {
                var infoMapPointer = Marshal.UnsafeAddrOfPinnedArrayElement(infoMapData, 0);
                var bitmap         = new Bitmap(width, height, PixelFormat.Format24bppRgb);
                if (!NativeMethods.GetInfoMap(mapName, name, infoMapPointer, bytesPerPixel))
                {
                    throw new UnitSyncException("GetInfoMap " + name + " failed");
                }
                var       bitmapData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, bitmap.PixelFormat);
                const int PixelSize  = 3;
                var       p          = (byte *)bitmapData.Scan0;
                for (var i = 0; i < infoMapData.Length; i++)
                {
                    var v = infoMapData[i];
                    var d = i / width * bitmapData.Stride + i % width * PixelSize;
                    p[d] = p[d + 1] = p[d + 2] = v;
                }
                bitmap.UnlockBits(bitmapData);
                return(bitmap);
            }
            finally
            {
                infoMapHandle.Free();
            }
        }

        string GetMapArchive(string mapName)
        {
            var i = NativeMethods.GetMapArchiveCount(mapName);

            if (i <= 0)
            {
                return(null);
            }
            var archivePath = NativeMethods.GetMapArchiveName(0);

            if (archivePath == null)
            {
                throw new UnitSyncException(NativeMethods.GetNextError());
            }
            return(Path.GetFileName(archivePath));
        }

        uint GetMapChecksum(string mapName)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            try
            {
                return(GetMapHashes().Single(kvp => kvp.Value == mapName).Key);
            }
            catch (InvalidOperationException)
            {
                throw new UnitSyncException("Map not found");
            }
        }

        MapInfo GetMapInfo(string mapName, int mapInfoVersion)
        {
            return(GetMapInfo(GetMapChecksum(mapName), mapInfoVersion));
        }

        MapInfo GetMapInfo(uint checksum)
        {
            return(GetMapInfo(checksum, DefaultMapInfoVersion));
        }

        MapInfo GetMapInfo(uint checksum, int mapInfoVersion)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            if (maps == null)
            {
                GetMapHashes();
            }
            var mapName = maps[checksum];

            if (!new[] { 0, 1 }.Contains(mapInfoVersion))
            {
                throw new ArgumentOutOfRangeException("mapInfoVersion", "must be 0 or 1.");
            }
            if (!GetMapHashes().ContainsValue(mapName))
            {
                throw new UnitSyncException(String.Format("Map not found ({0}).", mapName));
            }
            var mapInfo = new MapInfo {
                author = new String(' ', AuthorBufferSize), description = new String(' ', DescriptionBufferSize)
            };

            if (!NativeMethods.GetMapInfoEx(mapName, ref mapInfo, mapInfoVersion))
            {
                throw new UnitSyncException("Error getting map information.");
            }
            TestMapInfo(mapInfo);
            return(mapInfo);
        }

        MapInfo GetMapInfo(string mapName)
        {
            return(GetMapInfo(mapName, DefaultMapInfoVersion));
        }

        MapInfo GetMapInfoFromArchive(string mapArchive)
        {
            return(GetMapInfo(GetMapArchive(Path.GetFileName(mapArchive))));
        }

        string GetMapNameFromArchive(string archiveName)
        {
            var name = GetMapHashes().Values.FirstOrDefault(mapName => GetMapArchive(mapName) == archiveName);

            TraceErrors();
            return(name);
        }

        IEnumerable <Option> GetMapOptions(string mapName, string archiveName)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            if (archiveName == null)
            {
                throw new ArgumentNullException("archiveName");
            }
            NativeMethods.AddAllArchives(archiveName);
            NativeMethods.AddAllArchives("maphelper.sdz");
            var optionCount = NativeMethods.GetMapOptionCount(mapName);

            for (var optionIndex = 0; optionIndex < optionCount; optionIndex++)
            {
                var option = LoadOption(optionIndex);
                if (option != null)
                {
                    yield return(option);
                }
                TraceErrors();
            }
        }

        string GetModArchiveName(string name)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            int modIndex = NativeMethods.GetPrimaryModIndex(name);

            if (modIndex < 0)
            {
                throw new UnitSyncException(string.Format("Mod not found ({0}).", name));
            }
            string modArchive = NativeMethods.GetPrimaryModArchive(modIndex);

            return(modArchive);
        }

        Dictionary <string, string> GetModArchives()
        {
            var archives = new Dictionary <string, string>();

            foreach (var modName in GetModNames())
            {
                archives[GetModArchiveName(modName)] = modName;
            }
            return(archives);
        }

        IEnumerable <string> GetModDependencies(int modIndex)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }

            var ret   = new List <string>();
            var count = NativeMethods.GetPrimaryModArchiveCount(modIndex);

            for (var i = 0; i < count; i++)
            {
                ret.Add(GetModNameFromArchive(Path.GetFileName(NativeMethods.GetPrimaryModArchiveList(i))));
            }
            return(ret);
        }

        string GetModNameFromArchive(string archiveName)
        {
            string modName = null;

            for (var i = NativeMethods.GetPrimaryModCount() - 1; i >= 0; i--)             //check from last because modname is sorted by version and latest version should be last. ArchiveScanner.cpp line 1007
            {
                if (GetModArchiveName(NativeMethods.GetPrimaryModName(i)) == archiveName)
                {
                    modName = NativeMethods.GetPrimaryModName(i);                     //this archiveName (filename) is matched to the filename of this mod. We found it!
                    break;
                }
            }
            return(modName);

            //var archives = GetModArchives(); //inefficient because it get all archive first before searching
            //string modName;
            //return archives.TryGetValue(archiveName, out modName) ? modName : null;
        }

        IEnumerable <Option> GetModOptions(string archiveName)
        {
            NativeMethods.AddAllArchives(archiveName);
            var optionCount = NativeMethods.GetModOptionCount();

            for (var optionIndex = 0; optionIndex < optionCount; optionIndex++)
            {
                var option = LoadOption(optionIndex);
                if (option != null)
                {
                    yield return(option);
                }
                TraceErrors();
            }
        }

        /// <summary>
        /// Call AddAllArchives before this
        /// Icons not found are null
        /// </summary>
        byte[][] GetSideIcons(IEnumerable <string> sides)
        {
            return(sides.Select(side => ReadVfsFile("SidePics\\" + side + ".bmp")).ToArray());
        }

        Bitmap GetSquareMinimap(string mapName, int mipLevel)
        {
            if (!GetMapHashes().ContainsValue(mapName))
            {
                throw new UnitSyncException(string.Format("Map not found ({0}).", mapName));
            }
            if (mipLevel < 0 || mipLevel > MaxMipLevel)
            {
                throw new ArgumentOutOfRangeException("mipLevel", string.Format("Mip level must range from 0 to {0}.", MaxMipLevel));
            }

            var size    = 1024 >> mipLevel;
            var pointer = NativeMethods.GetMinimap(mapName, mipLevel);
            const PixelFormat format = PixelFormat.Format16bppRgb565;
            var pixelFormatSize      = Image.GetPixelFormatSize(format) / 8;
            var stride = size * pixelFormatSize;

            return(new Bitmap(size, size, stride, format, pointer));
        }

        Dictionary <string, string> GetStartUnits(string modName, out string[] sides)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            int modIndex = NativeMethods.GetPrimaryModIndex(modName);

            if (modIndex < 0)
            {
                throw new UnitSyncException("Mod not found (" + modName + ").");
            }
            return(GetStartUnits(modIndex, out sides));
        }

        Dictionary <string, string> GetStartUnits(int modIndex, out string[] sides)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            LoadModArchive(modIndex);
            var sideCount  = NativeMethods.GetSideCount();
            var startUnits = new Dictionary <string, string>();

            sides = new string[sideCount];
            for (var sideIndex = 0; sideIndex < sideCount; sideIndex++)
            {
                var sideName = NativeMethods.GetSideName(sideIndex);
                sides[sideIndex]     = sideName;
                startUnits[sideName] = NativeMethods.GetSideStartUnit(sideIndex);
            }
            return(startUnits);
        }

        IEnumerable <UnitInfo> GetUnitList(string modName)
        {
            int modIndex = NativeMethods.GetPrimaryModIndex(modName);

            if (modIndex < 0)
            {
                throw new UnitSyncException(string.Format("Mod not found ({0}).", modName));
            }
            return(GetUnitList(modIndex));
        }

        IEnumerable <UnitInfo> GetUnitList(int modIndex)
        {
            if (modIndex != loadedArchiveIndex)
            {
                NativeMethods.AddAllArchives(NativeMethods.GetPrimaryModArchive(modIndex));
                loadedArchiveIndex = modIndex;
            }
            for (var i = 0; i <= MaxUnits && NativeMethods.ProcessUnitsNoChecksum() > 0; i++)
            {
                var error = NativeMethods.GetNextError();
                if (error != null)
                {
                    Trace.TraceWarning("UnitSync Error: " + error);
                    break;
                }
            }
            for (var i = 0; i < NativeMethods.GetUnitCount(); i++)
            {
                yield return(new UnitInfo(NativeMethods.GetUnitName(i), NativeMethods.GetFullUnitName(i)));
            }
        }

        ListOption LoadListOption(int optionIndex, int itemIndex)
        {
            var listOption = new ListOption
            {
                Name        = NativeMethods.GetOptionListItemName(optionIndex, itemIndex),
                Description = NativeMethods.GetOptionListItemDesc(optionIndex, itemIndex),
                Key         = NativeMethods.GetOptionListItemKey(optionIndex, itemIndex)
            };

            TraceErrors();
            return(listOption);
        }

        void LoadModArchive(int modIndex)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            if (modIndex == loadedArchiveIndex)
            {
                return;
            }
            NativeMethods.AddAllArchives(NativeMethods.GetPrimaryModArchive(modIndex));
            loadedArchiveIndex = modIndex;
        }

        Option LoadOption(int index)
        {
            TraceErrors();
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            var option = new Option
            {
                Name        = NativeMethods.GetOptionName(index),
                Key         = NativeMethods.GetOptionKey(index),
                Description = NativeMethods.GetOptionDesc(index),
                Type        = (OptionType)NativeMethods.GetOptionType(index),
                Scope       = NativeMethods.GetOptionScope(index),
                Section     = NativeMethods.GetOptionSection(index),
                Style       = NativeMethods.GetOptionStyle(index)
            };

            switch (option.Type)
            {
            case OptionType.Bool:
                option.Default = NativeMethods.GetOptionBoolDef(index).ToString();
                break;

            case OptionType.Number:
                option.Default = NativeMethods.GetOptionNumberDef(index).ToString(CultureInfo.InvariantCulture);
                option.Min     = NativeMethods.GetOptionNumberMin(index);
                option.Max     = NativeMethods.GetOptionNumberMax(index);
                option.Step    = NativeMethods.GetOptionNumberStep(index);
                break;

            case OptionType.String:
                option.Default   = NativeMethods.GetOptionStringDef(index);
                option.StrMaxLen = NativeMethods.GetOptionStringMaxLen(index);
                break;

            case OptionType.List:
                option.Default = NativeMethods.GetOptionListDef(index);
                var listCount = NativeMethods.GetOptionListCount(index);
                for (var i = 0; i < listCount; i++)
                {
                    var optl = LoadListOption(index, i);
                    option.ListOptions.Add(optl);
                }
                break;

            case OptionType.Section:
                break;

            default:
                return(null);
            }
            TraceErrors();
            return(option);
        }

        /// <summary>
        /// Call AddAllArchives before this
        /// </summary>
        byte[] ReadVfsFile(string filePath)
        {
            if (filePath == null)
            {
                throw new ArgumentNullException("filePath");
            }
            if (disposed)
            {
                throw new ObjectDisposedException("Unitsync has already been released.");
            }
            var handle = NativeMethods.OpenFileVFS(filePath);

            try
            {
                if (handle == 0)
                {
                    Trace.TraceWarning("File " + filePath + " not found in VFS.");
                    return(null);
                }
                var fileSize = NativeMethods.FileSizeVFS(handle);
                if (fileSize == 0)
                {
                    return(new byte[0]);
                }
                if (fileSize < 0)
                {
                    return(null);
                }
                var fileData  = new byte[fileSize];
                var bytesRead = NativeMethods.ReadFileVFS(handle, fileData, fileSize);
                if (bytesRead < 0)
                {
                    TraceErrors();
                    return(null);
                }
                if (bytesRead != fileSize)
                {
                    Trace.TraceWarning("File size and bytes read mismatch for " + filePath);
                }
                return(fileData);
            }
            finally
            {
                if (handle != 0)
                {
                    NativeMethods.CloseFileVFS(handle);
                }
            }
        }