public static GameSave Load(Stream save, int expectedLength = -1) { var gameSave = new GameSave(); using (var reader = new SaveReader(save)) { #region Basic Game Information (Header, Expansions, Mods) #region Header var fileStart = new String(reader.ReadChars(4)); if (fileStart != "CIV5") { throw new InvalidSaveException("File did not start with CIV5"); } reader.VerifySectionDelimiter(0x08); gameSave.Version = reader.ReadSaveString(); gameSave.Build = reader.ReadSaveString(); gameSave.TurnNumber = reader.ReadInt32(); gameSave.GameMode = (GameMode)reader.ReadByte(); gameSave.HeaderCiv = reader.ReadCiv(); gameSave.HeaderDifficulty = reader.ReadDifficulty(); //Save these for now. We can set them later when we know what the correct GameProperty is based on expansions. var headerStartingEra = reader.ReadEra(); var currentEra = reader.ReadEra(); gameSave.GamePace.Value = reader.ReadPace(); var worldSize = reader.ReadMapSize(); gameSave.Map = reader.ReadMap(worldSize); #endregion #region Expansions int numberOfExpansions = reader.ReadInt32(); for (int i = 0; i < numberOfExpansions; i++) { byte[] expansionId = reader.ReadBytes(16); reader.ReadInt32(); var expansionName = reader.ReadSaveString(); var expansion = Expansion.Expansion.AllWithInternal.SingleOrDefault(e => Enumerable.SequenceEqual(e.SaveId, expansionId) && e.SaveName == expansionName); if (expansion == null) { expansion = new Expansion.Expansion(expansionName, expansionName, String.Empty, GameSteamIds.CivV, expansionId); } gameSave.AddExpansion(expansion); } gameSave.HeaderStartingEra.Value = headerStartingEra; gameSave.HeaderCurrentEra.Value = currentEra; #endregion #region Mods int numberOfMods = reader.ReadInt32(); for (int i = 0; i < numberOfMods; i++) { string modId = reader.ReadSaveString(); reader.ReadInt32(); string modName = reader.ReadSaveString(); gameSave.Mods.Add(new Mod(modName, modId)); } #endregion #endregion #region Nothing Important #region Unknown reader.SkipSaveStrings(2); reader.ReadPlayerColor(); reader.ReadBytes(HeaderCrazyBytes.Length); reader.SkipInt32s(4); reader.ReadSaveString(); #endregion reader.SkipIntSection(); #endregion #region Player Information #region Names reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { gameSave.Players[i].Name = reader.ReadSaveString(); } #endregion #region Types reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { gameSave.Players[i].Type = (PlayerType)reader.ReadInt32(); } #endregion #region Slots reader.VerifySectionDelimiter(); int slot; int playerSlotCount = -1; do { playerSlotCount++; slot = reader.ReadInt32(); } while (slot == 0x02); reader.SkipInt32s(SaveHelpers.StandardSectionBlockCount - playerSlotCount - 1); #endregion #region Teams reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { gameSave.Players[i].Team = reader.ReadInt32(); } #endregion #region Difficulties reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { gameSave.Players[i].Difficulty = (PlayerDifficulty)reader.ReadInt32(); } #endregion #region Civilizations reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { gameSave.Players[i].Civilization = reader.ReadCiv(i >= SaveHelpers.MaxPlayers && i != SaveHelpers.StandardSectionBlockCount - 1); } #endregion #region Leaders reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { var leader = reader.ReadLeader(); if (gameSave.Players[i].Civilization != null) { gameSave.Players[i].Civilization.Leader = leader; } } #endregion #region Current Player Index reader.ReadInt32(); gameSave.CurrentPlayerIndex = reader.ReadInt32(); reader.SkipSaveStrings(3); #endregion #endregion #region Nothing Important reader.SkipByteSection(309); reader.SkipIntSection(); reader.SkipStringSection(); #endregion #region Player Passwords reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { gameSave.Players[i].Password = reader.ReadSaveString(); } #endregion #region Starting Era + Temperature Text Key + Unimportant Things #region Blank reader.VerifySectionDelimiter(); reader.SkipInt32s(67); #endregion reader.SkipTextKey(); #region Actual Starting Era reader.SkipInt32s(11); var startingEraIndex = reader.ReadInt32(); var startingEraList = gameSave.StartingEra.PossibleValues.Keys.ToList(); var startingEra = startingEraList[startingEraIndex]; gameSave.StartingEra.Value = startingEra; #endregion #region Blank reader.VerifySectionDelimiter(); reader.SkipInt32s(65); #endregion #region Blank reader.VerifySectionDelimiter(); reader.SkipSaveStrings(SaveHelpers.StandardSectionBlockCount); #endregion #region Blank reader.VerifySectionDelimiter(0x07); reader.ReadBytes(7); #endregion #endregion #region Game Name + Time Offset + Turn Number reader.VerifySectionDelimiter(SaveHelpers.FullBlock); gameSave.Name = reader.ReadSaveString(); gameSave.TimeOffset = reader.ReadInt32(); gameSave.GameStarted = reader.ReadBoolean(); gameSave.TurnNumber = reader.ReadInt32(); reader.ReadBytes(9); #endregion #region Player Difficulties reader.VerifySectionDelimiter(); reader.SkipInt32s(SaveHelpers.StandardSectionBlockCount); #endregion #region Nothing Important reader.SkipByteSection(258); reader.SkipIntSection(); #endregion #region City-State Stuff + Random Seed #region Random Seed + Map Path + City-State Count reader.SkipSaveStrings(2); reader.ReadByte(); gameSave.RandomSeed = reader.ReadInt32(); reader.ReadBytes(2); reader.ReadSaveString(); reader.SkipInt32s(2); gameSave.Map.NumberOfCityStates.Value = reader.ReadInt32(); #endregion #region City-States reader.VerifySectionDelimiter(); reader.SkipSaveStrings(22); for (int i = SaveHelpers.MaxPlayers; i < SaveHelpers.StandardSectionBlockCount - 1; i++) { var civ = reader.ReadCiv(true); if (gameSave.Players[i].Civilization != null && civ != null) { gameSave.Players[i].Civilization.Name = civ.Name; } } reader.ReadCiv(false); #endregion #region City-State Bits reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { bool isCityState = reader.ReadBoolean(); //This is a safeguard //It will be called if a player we thought was a civ is actually a city-state //So far, I have never found a save file where this happens if (isCityState && !(gameSave.Players[i].Civilization is CivilizationMinor)) { var civ = gameSave.Players[i].Civilization; gameSave.Players[i].Civilization = new CivilizationMinor(civ.Name, civ.Color); } } reader.ReadByte(); #endregion #endregion #region Turn Timer Seconds + Unimportant Things #region Unknown reader.VerifySectionDelimiter(0x04); reader.ReadBytes(4); #endregion reader.SkipIntSection(); #region Player Names reader.SkipStringSection(); #endregion #region Turn Timer Seconds reader.VerifySectionDelimiter(0x05); gameSave.TurnTimer = reader.ReadInt32(); #endregion reader.SkipByteSection(); #endregion #region Player Colors reader.VerifySectionDelimiter(); for (int i = 0; i < SaveHelpers.StandardSectionBlockCount; i++) { var color = reader.ReadPlayerColor(); if (gameSave.Players[i].Civilization != null) { gameSave.Players[i].Civilization.Color = color; } } #endregion #region Repeated Data + Unimportant Things reader.ReadBytes(10); #region Sea Level Text Key reader.VerifySectionDelimiter(); reader.SkipInt32s(19); reader.SkipTextKey(); reader.ReadBytes(5); #endregion #region Player Slots reader.SkipIntSection(); #endregion #region Player Types reader.SkipIntSection(); #endregion #region Random Seed reader.SkipInt32s(3); #endregion #region Player Teams reader.SkipIntSection(); #endregion #region Turn Timer Text Key reader.ReadBytes(9); reader.SkipTextKey(); reader.SkipInt32s(5); reader.ReadByte(); #endregion #endregion #region Victory Conditions reader.VerifySectionDelimiter(0x05); gameSave.TimeVictory = reader.ReadBoolean(); gameSave.ScienceVictory = reader.ReadBoolean(); gameSave.DominationVictory = reader.ReadBoolean(); gameSave.CulturalVictory = reader.ReadBoolean(); gameSave.DiplomaticVictory = reader.ReadBoolean(); #endregion #region Random Map Stuff reader.VerifySectionDelimiter(); reader.SkipInt32s(18); if (gameSave.HasGnkOrBnw) { reader.ReadInt32(); } reader.SkipTextKey(); var expectedCrazyMapBytes = SaveHelpers.GetExpectedCrazyMapSizeBytes(gameSave); reader.ReadBytes(expectedCrazyMapBytes.Length); #endregion #region Options #region Game Options int numberOfPrefs = reader.ReadInt32(); for (int i = 0; i < numberOfPrefs; i++) { reader.ReadPreference(gameSave); } #endregion #region Map Options int numberOfMapProps = reader.ReadInt32(); for (int i = 0; i < numberOfMapProps; i++) { reader.ReadMapPreference(gameSave); } #endregion #endregion #region Nothing Important #region Build Number reader.ReadSaveString(); #endregion reader.SkipByteSection(); reader.SkipByteSection(); #region Email Addresses (Ignore) reader.SkipStringSection(); #endregion #endregion #region Read Raw Data if (expectedLength < 0) { expectedLength = (int)save.Length; } gameSave.RawGameDataIndex = reader.Position; reader.ReadBytes((int)(expectedLength - save.Position)); gameSave._OriginalBytes = reader.AllBytesRead; #endregion } return(gameSave); }
public virtual void Save(Stream stream) { var output = new SaveWriter(stream, this); #region Basic Game Information (Header, Expansions, Mods) #region Header output.Write(SaveHelpers.FileStart); output.Write(0x08); output.Write(this.Version.Bytes); output.Write(this.Build.Bytes); WriteCurrentTurnNumber(output); output.Write((byte)this.GameMode); var headerCiv = HeaderCiv != null ? HeaderCiv.SaveName.Bytes : Players[0].Civilization != null ? Players[0].Civilization.SaveName.Bytes : BitConverter.GetBytes(0x00); output.Write(headerCiv); var headerDifficulty = HeaderDifficulty.HasValue ? HeaderDifficulty.Value : Players[0].Difficulty; output.Write(SaveHelpers.ConvertOptionEnumToSaveStr(headerDifficulty).Bytes); output.Write(SaveHelpers.ConvertOptionEnumToSaveStr(this.HeaderStartingEra.Value).Bytes); WriteCurrentEra(output); output.Write(SaveHelpers.ConvertOptionEnumToSaveStr(this.GamePace.Value).Bytes); output.Write(SaveHelpers.ConvertOptionEnumToSaveStr(this.Map.Size.Value).Bytes); output.Write(this.Map.Path.Bytes); #endregion #region Expansions output.Write(this._Expansions.Count); foreach (var expansion in this.Expansions) { output.Write(expansion.SaveId); output.Write(0x01); output.Write(expansion.SaveName.Bytes); } #endregion #region Mods output.Write(Mods.Count); foreach (var mod in this.Mods) { output.Write(mod.SaveId.Bytes); output.Write(0x01); output.Write(mod.SaveName.Bytes); } #endregion #endregion #region Nothing Important #region Unknown + Map Path output.WriteEmptyBlocks(2); WritePlayerColor(output); output.Write(HeaderCrazyBytes); output.Write(0x00); output.Write(0x03); output.Write(0x03); output.Write(0x02); output.Write(this.Map.Path.Bytes); #endregion output.Write(SaveHelpers.SectionDelimiter); output.WriteFullBlocks(SaveHelpers.StandardSectionBlockCount); #endregion #region Player Information output.WritePlayerNamesSection(); output.WritePlayerTypeSection(); output.WritePlayerSlotsSection(); output.WritePlayerTeamsSection(); output.WritePlayerDifficultiesSection(this.Expansions.Contains(Expansion.Expansion.BraveNewWorld)); WriteCivilizationsSection(output); WriteLeadersSection(output); #region Current Player Index output.Write(0x06); WriteCurrentPlayerIndex(output); output.WriteEmptyBlocks(3); #endregion #endregion #region Nothing Important #region Mostly Blank output.Write(SaveHelpers.SectionDelimiter); output.WriteFullBlocks(SaveHelpers.StandardSectionBlockCount); output.WriteEmptyBlocks(2); output.Write((byte)0x00); output.Write(0x02); output.Write(0x00); output.Write(SaveHelpers.FullBlock); output.WriteEmptyBlocks(8); #endregion output.WriteEmptySection(); output.WriteEmptySection(); #endregion WritePlayerPasswordsSection(output); #region Starting Era + Temperature Text Key + Unimportant Things #region Blank output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(67); #endregion output.WriteTextKey("CLIMATE", "Temperate"); #region Actual Starting Era output.Write(0x00); output.Write(0x05); output.Write(0x05); output.Write(0x19); output.WriteEmptyBlocks(5); output.Write(new byte[] { 0x33, 0x33, 0x73, 0x3F, 0x00, 0x00, 0x80, 0x3E }); var startingEra = this.StartingEra.Value; var startingEraList = this.StartingEra.PossibleValues.Keys.ToList(); int startingEraIndex = startingEraList.IndexOf(startingEra); output.Write(startingEraIndex); #endregion #region Blank output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(65); #endregion #region Blank output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(64); #endregion #region Blank output.Write(0x07); output.Write(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); #endregion #endregion #region Game Name + Time Offset + Turn Number output.WriteFullBlocks(1); output.Write(Name.Bytes); output.Write(TimeOffset); output.Write(GameStarted); WriteCurrentTurnNumber(output); output.Write((byte)0x02); output.WriteEmptyBlocks(2); #endregion output.WritePlayerDifficultiesSection(this.Expansions.Contains(Expansion.Expansion.BraveNewWorld)); #region Nothing Important #region Mostly Blank output.Write(SaveHelpers.SectionDelimiter); output.Write(0x03); output.WriteFullBlocks(SaveHelpers.StandardSectionBlockCount - 1); output.Write((byte)0x00); output.Write((byte)0x00); #endregion output.WriteEmptySection(); #endregion #region City-State Stuff + Random Seed #region Random Seed + Map Path + City-State Count output.WriteEmptyBlocks(2); output.Write((byte)0x00); output.Write(this.RandomSeed); output.Write((byte)0x00); output.Write((byte)0x00); output.Write(this.Map.Path.Bytes); output.WriteEmptyBlocks(2); output.Write(this.Map.NumberOfCityStates); #endregion #region City-States output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(SaveHelpers.MaxPlayers); WriteMinorCivs(output); output.Write(0x00); #endregion #region City-State Bits output.Write(SaveHelpers.SectionDelimiter); for (int i = 0; i < Players.Length; i++) { if (Players[i].Civilization != null && Players[i].Civilization is CivilizationMinor) { output.Write(true); } else { output.Write(false); } } output.Write((byte)0x00); #endregion #endregion #region Turn Timer Seconds + Unimportant Things #region Unknown output.Write(0x04); output.Write(0x00); #endregion output.WriteFullSection(); output.WritePlayerNamesSection(); #region Turn Timer Seconds output.Write(0x05); output.Write(this.TurnTimer); #endregion #region A Bunch of Bools //Maybe IsPlayer? The only false is the Barbarians output.Write(SaveHelpers.SectionDelimiter); for (int i = 0; i < SaveHelpers.SectionDelimiter - 1; i++) { output.Write(true); } output.Write(false); #endregion #endregion #region Player Colors WritePlayerColorsSection(output); #endregion #region Repeated Data + Unimportant Things output.Write(new byte[] { 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00 }); #region Sea Level Text Key output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(16); output.Write(0x01); output.Write(0x01); output.Write(0x00); output.WriteTextKey("SEALEVEL", "Medium"); output.Write(0x00); output.Write((byte)0x00); #endregion output.WritePlayerSlotsSection(); output.WritePlayerTypeSection(); #region Random Seed output.Write(0x00); output.Write(this.RandomSeed); output.Write(0x00); #endregion output.WritePlayerTeamsSection(); #region Turn Timer Text Key output.Write((byte)0x00); output.Write(0x04); output.Write(0x00); output.WriteTextKey("TURNTIMER", "Fast"); output.Write(0x19); output.Write(0x09); output.Write(0x03); output.Write(0x03); output.Write(0x04); output.Write((byte)0x00); #endregion #endregion #region Victory Conditions output.Write(0x05); output.Write(BitConverter.GetBytes(this.TimeVictory)); output.Write(BitConverter.GetBytes(this.ScienceVictory)); output.Write(BitConverter.GetBytes(this.DominationVictory)); output.Write(BitConverter.GetBytes(this.CulturalVictory)); output.Write(BitConverter.GetBytes(this.DiplomaticVictory)); #endregion #region Random Map Stuff output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(16); if (this.HasGnkOrBnw) { output.Write(0x02); } int mapSizeId = (((int)this.Map.Size.Value) / 2) - 1; output.Write(mapSizeId); output.Write(0x00); var worldSize = SaveHelpers.ConvertOptionEnumToSaveStr(this.Map.Size.Value); output.WriteTextKey(worldSize.Prefix, worldSize.Value, true); output.Write(SaveHelpers.GetExpectedCrazyMapSizeBytes(this)); #endregion #region Options #region Game Options //Number of options output.Write(GetNumberOfSettings() + CustomSettings.Count); output.WriteGamePreference("DYNAMIC_TURNS", DynamicTurns); output.WriteGamePreference("SIMULTANEOUS_TURNS", SimultaneousTurns); output.WriteGamePreference("PITBOSS", Pitboss); output.WriteGamePreference("QUICK_COMBAT", QuickCombat); output.WriteGamePreference("QUICK_MOVEMENT", QuickMovement); output.WriteGamePreference("END_TURN_TIMER_ENABLED", EnableTurnTimer); output.WriteGamePreference("POLICY_SAVING", AllowPolicySaving); output.WriteGamePreference("PROMOTION_SAVING", AllowPromotionSaving); output.WriteGamePreference("COMPLETE_KILLS", CompleteKills); output.WriteGamePreference("DISABLE_START_BIAS", DisableStartBias); output.WriteGamePreference("NEW_RANDOM_SEED", NewRandomSeed); output.WriteGamePreference("NO_GOODY_HUTS", NoAncientRuins); output.WriteGamePreference("NO_BARBARIANS", NoBarbarians); output.WriteGamePreference("NO_CITY_RAZING", NoCityRazing); output.WriteGamePreference("ONE_CITY_CHALLENGE", OneCityChallenge); output.WriteGamePreference("RAGING_BARBARIANS", RagingBarbarians); output.WriteGamePreference("RANDOM_PERSONALITIES", RandomPersonalities); output.WriteGamePreference("ALWAYS_WAR", AlwaysWar); output.WriteGamePreference("ALWAYS_PEACE", AlwaysPeace); output.WriteGamePreference("NO_CHANGING_WAR_PEACE", NoChangingWarPeace); output.WriteGamePreference("LOCK_MODS", LockMods); output.WriteGamePreference("NO_SCIENCE", NoScience); output.WriteGamePreference("NO_POLICIES", NoPolicies); output.WriteGamePreference("NO_HAPPINESS", NoHappiness); if (HasGnkOrBnw) { output.WriteGamePreference("NO_ESPIONAGE", NoEspionage); output.WriteGamePreference("NO_RELIGION", NoReligion); } if (_Expansions.Contains(Expansion.Expansion.BraveNewWorld)) { output.WriteGamePreference("NO_LEAGUES", NoLeagues); output.WriteGamePreference("NO_CULTURE_OVERVIEW_UI", NoCultureOverviewUI); } foreach (var setting in CustomSettings) { output.WriteGamePreference(setting.Key, setting.Value); } #endregion #region Map Options var mapProperties = this.Map.MapProperties; output.Write(mapProperties.Count); int index = 1; foreach (var prop in mapProperties) { object value = prop.Value; var possibleValues = prop.PossibleValues.Keys.ToList(); var propIndex = possibleValues.IndexOf(value) + 1; var strProp = new SaveString(index.ToString()); output.Write(strProp.Bytes); output.Write(propIndex); index++; } #endregion #endregion #region Nothing Important #region Build Number var buildStr = new SaveString(this.Build + " FINAL RELEASE"); output.Write(buildStr.Bytes); #endregion output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(16); output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(16); #region Email Addresses (Ignore) output.Write(SaveHelpers.SectionDelimiter); output.WriteEmptyBlocks(64); #endregion #endregion WriteRawGameData(output); }
protected virtual void WriteCurrentEra(SaveWriter output) { output.Write(SaveHelpers.ConvertOptionEnumToSaveStr(this.HeaderStartingEra.Value).Bytes); }
protected override void WriteCurrentEra(SaveWriter output) { output.Write(SaveHelpers.ConvertOptionEnumToSaveStr(HeaderCurrentEra.Value).Bytes); }