/// <summary> /// Erstellt aus der angegebenen Binär-Datei eine neue External-File-Struktur. /// </summary> /// <param name="data">Die auszulesende Binär-Datei.</param> /// <returns></returns> /// <remarks></remarks> public static ExternalFile FromBinary(RAMBuffer data) { // An den Anfang springen data.Position = 0; // Der Rückgabewert ExternalFile ret = new ExternalFile(); // Ausnahmen abfangen für ungültige Dateien try { // DRS-Dateiname uint drsNameLen = data.ReadUInteger(); ret.DRSFile = System.Text.Encoding.ASCII.GetString(data.ReadByteArray((int)drsNameLen)); // Datei-ID ret.FileID = data.ReadUInteger(); // Ressourcen-Typ uint resTypeLen = data.ReadUInteger(); ret.ResourceType = System.Text.Encoding.ASCII.GetString(data.ReadByteArray((int)resTypeLen)); // Data uint dataLen = data.ReadUInteger(); ret.Data = data.ReadByteArray((int)dataLen); } catch { // Fehler MessageBox.Show("Fehler: Das Datenobjekt konnte nicht fehlerfrei in ein ExternalFile-Objekt umgewandelt werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error); } // Fertig return(ret); }
/// <summary> /// Erstellt eine neue Farbtabelle aus derm Bitmap-Puffer (d.h. aus einer binären Bitmap-Tabelle). /// </summary> /// <param name="buffer">Der Bitmap-Puffer mit einem Zeiger am Beginn der Farbtabelle.</param> /// <param name="count">Die Anzahl der in der Tabelle definierten Farben.</param> public ColorTable(ref RAMBuffer buffer, uint count) { // Einträge einzeln einlesen for (int i = 0; i < count; ++i) { // 4 Bytes lesen byte[] b = buffer.ReadByteArray(4); // Farbeintrag erstellen _colors[i] = Color.FromArgb(b[2], b[1], b[0]); } // Fehlende Einträge mit 255er-Weiß initialisieren for (int i = (int)count; i < 256; ++i) { // Weiß einfügen _colors[i] = Color.FromArgb(255, 255, 255); } }
/// <summary> /// Reads the data element from the given buffer. /// </summary> /// <param name="buffer">The buffer where the data element should be deserialized from.</param> public Diplomacy ReadData(RAMBuffer buffer) { StancesPerPlayer = new List <StancesToPlayers>(16); for (int i = 0; i < 16; ++i) { StancesPerPlayer.Add(new StancesToPlayers().ReadData(buffer)); } // Separator buffer.ReadByteArray(11520); ScenarioDataElementTools.AssertTrue(buffer.ReadUInteger() == 0xFFFFFF9D); AlliedVictoryObsolete = new List <uint>(16); for (int i = 0; i < 16; ++i) { AlliedVictoryObsolete.Add(buffer.ReadUInteger()); } return(this); }
/// <summary> /// Lädt die Bitmap-Datei aus dem angegebenen Puffer. /// </summary> /// <param name="buffer">Der Puffer, aus dem die Bitmap-Datei gelesen werden soll.</param> /// <param name="pal">Optional. Gibt die zu verwendende 256er-Farbtabelle an. Sonst wird die entweder die im Bitmap angegebene oder die 50500er-Farbtabelle verwendet.</param> /// <param name="readFileHeader">Optional. Gibt an, ob der Dateiheader gelesen werden oder direkt mit der BITMAPINFOHEADER-Struktur begonnen werden soll.</param> public BitmapLoader(RAMBuffer buffer, JASCPalette pal = null, bool readFileHeader = true) { // Header laden _header = new Header(); if (readFileHeader) { _header.type = buffer.ReadUShort(); _header.fileSize = buffer.ReadUInteger(); _header.reserved = buffer.ReadUInteger(); _header.offsetData = buffer.ReadUInteger(); } else { _header.type = 0x424D; _header.fileSize = 0; _header.reserved = 0; _header.offsetData = 54; } _header.imageHeaderSize = buffer.ReadUInteger(); _header.width = buffer.ReadInteger(); _header.height = buffer.ReadInteger(); _header.layerCount = buffer.ReadUShort(); _header.bitsPerPixel = buffer.ReadUShort(); _header.compression = buffer.ReadUInteger(); _header.size = buffer.ReadUInteger(); _header.xDPI = buffer.ReadInteger(); _header.yDPI = buffer.ReadInteger(); _header.colorCount = buffer.ReadUInteger(); _header.colorImportantCount = buffer.ReadUInteger(); // Farbtabellenanzahl nachjustieren if (_header.colorCount == 0 && _header.bitsPerPixel == 8) { _header.colorCount = 256; } // Farbtabelle laden bool needAdjustColorTable = false; if (_header.colorCount > 0) { // Bildfarbtabelle laden _colorTable = new ColorTable(ref buffer, _header.colorCount); // Falls eine Palette übergeben wurde, diese mit der Bildtabelle vergleichen if (pal == null || pal._farben.GetLength(0) != 256) { needAdjustColorTable = true; } else { for (int i = 0; i < 256; ++i) { // Farben vergleichen Color aktF = _colorTable[i]; if (pal._farben[i, 0] != aktF.R || pal._farben[i, 1] != aktF.G || pal._farben[i, 2] != aktF.B) { // Farbtabellen unterscheiden sich needAdjustColorTable = true; break; } } } } else { // Bei 24-Bit-Bitmaps wird die Farbtabelle später geladen _colorTable = null; } // Nach Bitzahl unterscheiden if (_header.bitsPerPixel == 8) { // Bilddatenbreite ggf. auf ein Vielfaches von 4 Bytes erhöhen int width = _header.width; // Hilfsvariable zur Performanceerhöhung (immer gleichwertig mit _header.width) int width2 = width; while (width2 % 4 != 0) { width2++; } // Binäre Original-Bilddaten einlesen _imageDataBin = buffer.ReadByteArray(width2 * Math.Abs(_header.height)); // Neues Bilddaten-Array anlegen (ohne Füllbytes) _imageData = new byte[width * Math.Abs(_header.height)]; // Richtung bestimmen bool dirTopDown = (_header.height < 0); // Der bisher nächste Farbindex byte nearestIndex = 0; // Der Abstand zum bisher nächsten Farbindex double nearestDistance; // Der aktuelle Farbabstand double tempDistance = 0.0; // Bilddaten abrufen int height2 = Math.Abs(_header.height); for (int x = 0; x < width2; x++) { for (int y = 0; y < height2; y++) { // Wenn es sich bei dem aktuellen Pixel um kein Füllbyte handelt, diesen übernehmen if (x < width) { // Pixel abrufen byte aktCol = _imageDataBin[y * width2 + x]; // TODO: 0-Indizes in 255 umwandeln?? // Falls nötig, Farben vergleichen if (needAdjustColorTable) { // Alle Farbwerte abrufen byte aktB = _colorTable[aktCol].B; byte aktG = _colorTable[aktCol].G; byte aktR = _colorTable[aktCol].R; // Die zur Pixelfarbe nächste Palettenfarbe suchen { // Werte zurücksetzen nearestIndex = 0; nearestDistance = 441.673; // Anfangswert: maximaler möglicher Abstand // Alle Einträge durchgehen for (int i = 0; i < 256; i++) { // Aktuelle Paletten-RGB-Werte abrufen byte pR = pal._farben[i, 0]; byte pG = pal._farben[i, 1]; byte pB = pal._farben[i, 2]; // Gleiche Einträge sofort filtern if (aktR == pR && aktB == pB && aktG == pG) { // Paletten-Index überschreiben nearestIndex = (byte)i; // Fertig break; } // Abstand berechnen (Vektorlänge im dreidimensionalen RGB-Farbraum) tempDistance = Math.Sqrt(Math.Pow(aktR - pR, 2) + Math.Pow(aktG - pG, 2) + Math.Pow(aktB - pB, 2)); // Vergleichen if (tempDistance < nearestDistance) { // Index merken nearestDistance = tempDistance; nearestIndex = (byte)i; } } // Paletten-Index überschreiben aktCol = nearestIndex; } } // Ende Adjust-ColorTable // Pixel zum Hauptbildarray hinzufügen und dabei nach Top-Down / Bottom-Up unterscheiden _imageData[(dirTopDown ? y : height2 - y - 1) * width + x] = aktCol; } } } } else if (_header.bitsPerPixel == 24) { // Es handelt sich um ein 24-Bit-Bitmap, somit muss eine Farbtabelle eingeführt werden { // Farbpalettenreader abrufen JASCPalette tempPal; if (pal == null) { tempPal = new JASCPalette(new RAMBuffer(BitmapLibrary.Properties.Resources.pal50500)); } else { tempPal = pal; } // Farbpaletteninhalt in eigene Farbtabelle schreiben _colorTable = new ColorTable(); for (int i = 0; i < tempPal._farben.GetLength(0); i++) { // Eintrag in Tabelle einfügen _colorTable[i] = Color.FromArgb(tempPal._farben[i, 0], tempPal._farben[i, 1], tempPal._farben[i, 2]); // Sicherheitshalber bei i = 255 abbrechen (falls Palette zu groß sein sollte) if (i == 255) { break; } } } // Bilddatenbreite ggf. auf ein Vielfaches von 4 Bytes erhöhen int width = _header.width; // Hilfsvariable zur Performanceerhöhung (immer gleichwertig mit _header.width) int fillBytes = 0; while (((width * 3) + fillBytes) % 4 != 0) { fillBytes++; } // Binäre Original-Bilddaten einlesen _imageDataBin = buffer.ReadByteArray((3 * width + fillBytes) * Math.Abs(_header.height)); // Neues Bilddaten-Array anlegen (ohne Füllbytes) _imageData = new byte[width * Math.Abs(_header.height)]; // Richtung bestimmen bool dirTopDown = (_header.height < 0); // Der bisher nächste Farbindex byte nearestIndex = 0; // Der Abstand zum bisher nächsten Farbindex double nearestDistance; // Der aktuelle Farbabstand double tempDistance = 0.0; // Bilddaten abrufen int height2 = Math.Abs(_header.height); for (int x = 0; x < width; x++) { for (int y = 0; y < height2; y++) { // Pixel abrufen byte aktB = _imageDataBin[y * (3 * width + fillBytes) + 3 * x]; byte aktG = _imageDataBin[y * (3 * width + fillBytes) + 3 * x + 1]; byte aktR = _imageDataBin[y * (3 * width + fillBytes) + 3 * x + 2]; // Die zur Pixelfarbe nächste Palettenfarbe suchen { // Werte zurücksetzen nearestIndex = 0; nearestDistance = 441.673; // Anfangswert: maximaler möglicher Abstand // Alle Einträge durchgehen for (int i = 0; i < 256; i++) { // Aktuelle Paletten-RGB-Werte abrufen byte pR = _colorTable[i].R; byte pG = _colorTable[i].G; byte pB = _colorTable[i].B; // Gleiche Einträge sofort filtern if (aktR == pR && aktB == pB && aktG == pG) { // Pixel zum Hauptbildarray hinzufügen und dabei nach Top-Down / Bottom-Up unterscheiden _imageData[(dirTopDown ? y : height2 - y - 1) * width + x] = (byte)i; // Fertig break; } // Abstand berechnen (Vektorlänge im dreidimensionalen RGB-Farbraum) tempDistance = Math.Sqrt(Math.Pow(aktR - pR, 2) + Math.Pow(aktG - pG, 2) + Math.Pow(aktB - pB, 2)); // Vergleichen if (tempDistance < nearestDistance) { // Index merken nearestDistance = tempDistance; nearestIndex = (byte)i; } } // Pixel zum Hauptbildarray hinzufügen und dabei nach Top-Down / Bottom-Up unterscheiden _imageData[(dirTopDown ? y : height2 - y - 1) * width + x] = nearestIndex; } } // Ggf. Füllbytes überspringen (bei Dateiende nicht) if (buffer.Position < buffer.Length - fillBytes) { buffer.Position = (buffer.Position + fillBytes); } } } }
/// <summary> /// Export die enthaltenen SLPs in den gegebenen Ordner. /// Reihenfolge der Exports ist wichtig - bei Änderungen im TechTreeEditor-Plugin gegenprüfen! /// </summary> /// <param name="folder">Der Zielordner.</param> /// <param name="baseId">Die erste freie ID, zu der exportiert werden soll.</param> /// <param name="broadside">Gibt an, ob die Grafiken zusätzlich im Breitseitenmodus exportiert werden sollen.</param> /// <param name="enabledCivSets">Eine Liste mit den zu exportierenden Kultur-Grafiksets.</param> public void Export(string folder, int baseId, bool broadside, List <Civ> enabledCivSets) { // Die aktuell freie ID int currId = baseId; // Basispfad bauen string baseFileName = Path.Combine(folder, Name); // Der XML-Projektdatei-Code string xmlCode = ""; // Hilfsfunktion fürs Exportieren Action <SLPFile, string> exportSlp = (slp, suffix) => { // Exportstruktur anlegen DRSLibrary.DRSFile.ExternalFile extFile = new DRSLibrary.DRSFile.ExternalFile(); // Breitseitenmodus? SLPFile slpE = slp; if (broadside) { // Suffix erweitern suffix += " [B]"; // Neue SLP mit umkopierten Frames erzeugen slp.writeData(); RAMBuffer tmpBuffer = (RAMBuffer)slp.DataBuffer; tmpBuffer.Position = 0; slpE = new SLPFile(tmpBuffer); slpE._frameInformationHeaders.Clear(); slpE._frameInformationData.Clear(); // Drehrichtungen vorne bis links (-> links bis hinten) for (int i = 4; i <= 8; ++i) { // Alle Frames der Drehrichtung kopieren for (int f = i * (int)(slp.FrameCount / 9); f < (i + 1) * (int)(slp.FrameCount / 9); ++f) { // Header kopieren SLPFile.FrameInformationHeader fHead = new SLPFile.FrameInformationHeader(); fHead.AnchorX = slp._frameInformationHeaders[f].AnchorX; fHead.AnchorY = slp._frameInformationHeaders[f].AnchorY; fHead.Width = slp._frameInformationHeaders[f].Width; fHead.Height = slp._frameInformationHeaders[f].Height; fHead.Properties = slp._frameInformationHeaders[f].Properties; slpE._frameInformationHeaders.Add(fHead); // Daten kopieren SLPFile.FrameInformationData fData = new SLPFile.FrameInformationData(); fData.BinaryCommandTable = new List <SLPFile.BinaryCommand>(slp._frameInformationData[f].BinaryCommandTable); fData.BinaryRowEdge = slp._frameInformationData[f].BinaryRowEdge; fData.CommandTable = slp._frameInformationData[f].CommandTable; fData.CommandTableOffsets = new uint[slp._frameInformationData[f].CommandTableOffsets.Length]; fData.RowEdge = slp._frameInformationData[f].RowEdge; slpE._frameInformationData.Add(fData); } } // Drehrichtungen links links hinten bis hinten (-> hinten hinten rechts bis rechts) for (int i = 7; i >= 4; --i) { // Alle Frames der Drehrichtung spiegeln und kopieren for (int f = i * (int)(slp.FrameCount / 9); f < (i + 1) * (int)(slp.FrameCount / 9); ++f) { // Header kopieren SLPFile.FrameInformationHeader fHead = new SLPFile.FrameInformationHeader(); fHead.AnchorX = (int)slp._frameInformationHeaders[f].Width - slp._frameInformationHeaders[f].AnchorX; fHead.AnchorY = slp._frameInformationHeaders[f].AnchorY; fHead.Width = slp._frameInformationHeaders[f].Width; fHead.Height = slp._frameInformationHeaders[f].Height; fHead.Properties = slp._frameInformationHeaders[f].Properties; slpE._frameInformationHeaders.Add(fHead); // Daten spiegeln und kopieren SLPFile.FrameInformationData fData = new SLPFile.FrameInformationData(); fData.BinaryCommandTable = new List <SLPFile.BinaryCommand>(); fData.BinaryRowEdge = new SLPFile.BinaryRowedge[slp._frameInformationData[f].BinaryRowEdge.Length]; for (int bre = 0; bre < fData.BinaryRowEdge.Length; ++bre) { fData.BinaryRowEdge[bre] = new SLPFile.BinaryRowedge(slp._frameInformationData[f].BinaryRowEdge[bre]._right, slp._frameInformationData[f].BinaryRowEdge[bre]._left); } fData.RowEdge = new ushort[fHead.Height, 2]; for (int re = 0; re < fHead.Height; ++re) { fData.RowEdge[re, 0] = slp._frameInformationData[f].RowEdge[re, 1]; fData.RowEdge[re, 1] = slp._frameInformationData[f].RowEdge[re, 0]; } fData.CommandTable = new int[fHead.Height, fHead.Width]; for (int h = 0; h < fHead.Height; ++h) { for (int w = 0; w < fHead.Width; ++w) { fData.CommandTable[h, fHead.Width - w - 1] = slp._frameInformationData[f].CommandTable[h, w]; } } fData.CommandTableOffsets = new uint[slp._frameInformationData[f].CommandTableOffsets.Length]; slpE.CreateBinaryCommandTable(fData, (int)fHead.Width, (int)fHead.Height, slpE._settings); slpE._frameInformationData.Add(fData); } } } // SLP-Daten lesen und zuweisen slpE.writeData(); RAMBuffer slpBuffer = (RAMBuffer)slpE.DataBuffer; slpBuffer.Position = 0; extFile.Data = slpBuffer.ReadByteArray(slpBuffer.Length); // Metadaten festlegen extFile.DRSFile = "graphics"; extFile.FileID = (uint)(currId++); extFile.ResourceType = "slp"; // Exportdatei speichern extFile.ToBinary().Save(Path.Combine(folder, extFile.FileID + ".res")); // XML-Code generieren xmlCode += "<ResFile>" + extFile.FileID + ".res</ResFile>\r\n"; // SLP speichern if (!string.IsNullOrEmpty(suffix)) { slpBuffer.Save(baseFileName + " " + suffix + ".slp"); } else { slpBuffer.Save(baseFileName + ".slp"); } }; // Schatten und Rumpf exportieren if (ShadowSlp != null) { exportSlp(ShadowSlp, "(Schatten)"); } if (BaseSlp != null) { exportSlp(BaseSlp, ""); } // Hilfsfunktion für Segel-Export (Reihenfolge wichtig für ID-Vergabe) Action <Civ> exportCivSails = (civ) => { // Segel exportieren if (Sails[Sail.SailType.MainGo].Used) { exportSlp(Sails[Sail.SailType.MainGo].SailSlps[civ], "(" + Sail.SailTypeNames[Sail.SailType.MainGo] + ") [" + CivNames[civ] + "]"); } if (Sails[Sail.SailType.Small1].Used) { exportSlp(Sails[Sail.SailType.Small1].SailSlps[civ], "(" + Sail.SailTypeNames[Sail.SailType.Small1] + ") [" + CivNames[civ] + "]"); } if (Sails[Sail.SailType.Mid1].Used) { exportSlp(Sails[Sail.SailType.Mid1].SailSlps[civ], "(" + Sail.SailTypeNames[Sail.SailType.Mid1] + ") [" + CivNames[civ] + "]"); } if (Sails[Sail.SailType.Large1].Used) { exportSlp(Sails[Sail.SailType.Large1].SailSlps[civ], "(" + Sail.SailTypeNames[Sail.SailType.Large1] + ") [" + CivNames[civ] + "]"); } if (Sails[Sail.SailType.MainStop].Used) { exportSlp(Sails[Sail.SailType.MainStop].SailSlps[civ], "(" + Sail.SailTypeNames[Sail.SailType.MainStop] + ") [" + CivNames[civ] + "]"); } if (Sails[Sail.SailType.Large2].Used) { exportSlp(Sails[Sail.SailType.Large2].SailSlps[civ], "(" + Sail.SailTypeNames[Sail.SailType.Large2] + ") [" + CivNames[civ] + "]"); } if (Sails[Sail.SailType.Mid2].Used) { exportSlp(Sails[Sail.SailType.Mid2].SailSlps[civ], "(" + Sail.SailTypeNames[Sail.SailType.Mid2] + ") [" + CivNames[civ] + "]"); } if (Sails[Sail.SailType.Small2].Used) { exportSlp(Sails[Sail.SailType.Small2].SailSlps[civ], "(" + Sail.SailTypeNames[Sail.SailType.Small2] + ") [" + CivNames[civ] + "]"); } }; // Segel kulturweise exportieren foreach (Civ civSet in enabledCivSets) { exportCivSails(civSet); } // XML-Code speichern File.WriteAllText(Path.Combine(folder, "projectdata" + (broadside ? "_b" : "") + ".xml"), xmlCode); // Ggf. noch die Nicht-Breitseiten-Frames exportieren if (broadside) { Export(folder, currId, false, enabledCivSets); } }
/// <summary> /// Fügt eine Ressource hinzu oder ersetzt diese. /// </summary> /// <param name="data">Die hinzuzufügende / zu ersetzende Ressource.</param> /// <param name="resourceID">Die ID der hinzuzufügenden / zu ersetzenden Ressource.</param> /// <param name="resourceType">Der Typ der Ressource, rückwärts geschrieben und drei Zeichen lang.</param> /// <remarks></remarks> public void AddReplaceRessource(RAMBuffer data, ushort resourceID, string resourceType) { // Neue Ressource? if (!_files.ContainsKey(resourceID)) { // Die zu ermittelnden Tabelleninfos TableInfo myTI = new TableInfo(); Table myT = new Table(); // Suchen der Tabelle mit dem passenden Ressourcen-Typen int i; bool found = false; for (i = 0; i < _tableInfos.Count; ++i) { // Passender Ressourcen-Typ? if (_tableInfos[i].ResourceType == resourceType) { // Tabelleninfos merken myTI = _tableInfos[i]; myT = _tables[i]; found = true; break; } } // Neue Tabelle erforderlich? if (!found) { // Tabelleninfo anlegen myTI.Unknown1 = (byte)'a'; myTI.TableOffset = 0; myTI.ResourceType = resourceType; myTI.FileCount = 0; _tableInfos.Add(myTI); // Neue Tabelle ist der einzige Eintrag myT.Entries = new List <TableEntry>(); _tables.Add(myT); } // Eine Datei mehr myTI.FileCount += 1; // Tabelleneintrag für die Datei erstellen TableEntry eintr = new TableEntry(); eintr.FileSize = (uint)data.Length; eintr.FileID = resourceID; myT.Entries.Add(eintr); // Datei speichern data.Position = 0; _files.Add(resourceID, data.ReadByteArray(data.Length)); // Tabelleninfos speichern _tableInfos[i] = myTI; _tables[i] = myT; } else { // Ressource ersetzen data.Position = 0; _files[resourceID] = data.ReadByteArray(data.Length); // Tabellen durchlaufen und passende Datei-ID suchen Table currTable; TableEntry currEntry; for (int i = 0; i < _tables.Count; ++i) { // Aktuelle Tabelle abrufen currTable = _tables[i]; // Eintrag mit passender Datei-ID suchen for (int j = 0; j < currTable.Entries.Count; ++j) { // Eintrg abrufen currEntry = currTable.Entries[j]; // Passende ID? if (currEntry.FileID == resourceID) { // Dateigröße ersetzen currEntry.FileSize = (uint)data.Length; _tables[i].Entries[j] = currEntry; // Fertig return; } } } } }
/// <summary> /// Reads the data element from the given buffer. /// </summary> /// <param name="buffer">The buffer where the data element should be deserialized from.</param> public Disables ReadData(RAMBuffer buffer) { List <int> disabledTechCountPerPlayer = new List <int>(16); for (int i = 0; i < 16; ++i) { disabledTechCountPerPlayer.Add(buffer.ReadInteger()); } DisabledTechsPerPlayer = new List <List <uint> >(16); for (int i = 0; i < 16; ++i) { if (disabledTechCountPerPlayer[i] < 0) { disabledTechCountPerPlayer[i] = 0; } DisabledTechsPerPlayer.Add(new List <uint>(disabledTechCountPerPlayer[i])); for (int j = 0; j < disabledTechCountPerPlayer[i]; ++j) { DisabledTechsPerPlayer[i].Add(buffer.ReadUInteger()); } buffer.ReadByteArray(4 * (30 - disabledTechCountPerPlayer[i])); } List <int> disabledUnitsCountPerPlayer = new List <int>(16); for (int i = 0; i < 16; ++i) { disabledUnitsCountPerPlayer.Add(buffer.ReadInteger()); } DisabledUnitsPerPlayer = new List <List <uint> >(16); for (int i = 0; i < 16; ++i) { if (disabledUnitsCountPerPlayer[i] < 0) { disabledUnitsCountPerPlayer[i] = 0; } DisabledUnitsPerPlayer.Add(new List <uint>(disabledUnitsCountPerPlayer[i])); for (int j = 0; j < disabledUnitsCountPerPlayer[i]; ++j) { DisabledUnitsPerPlayer[i].Add(buffer.ReadUInteger()); } buffer.ReadByteArray(4 * (30 - disabledUnitsCountPerPlayer[i])); } List <int> disabledBuildingsCountPerPlayer = new List <int>(16); for (int i = 0; i < 16; ++i) { disabledBuildingsCountPerPlayer.Add(buffer.ReadInteger()); } DisabledBuildingsPerPlayer = new List <List <uint> >(16); for (int i = 0; i < 16; ++i) { if (disabledBuildingsCountPerPlayer[i] < 0) { disabledBuildingsCountPerPlayer[i] = 0; } DisabledBuildingsPerPlayer.Add(new List <uint>(disabledBuildingsCountPerPlayer[i])); for (int j = 0; j < disabledBuildingsCountPerPlayer[i]; ++j) { DisabledBuildingsPerPlayer[i].Add(buffer.ReadUInteger()); } buffer.ReadByteArray(4 * (20 - disabledBuildingsCountPerPlayer[i])); } Unused1 = buffer.ReadUInteger(); Unused2 = buffer.ReadUInteger(); FullTechMode = buffer.ReadUInteger(); StartingAges = new List <int>(16); for (int i = 0; i < 16; ++i) { StartingAges.Add(buffer.ReadInteger()); } return(this); }