private void WriteMapDataTo(BinaryWriter writer, AssetNameCollection assetNames) { writer.Write(assetNames.GetOrCreateAssetIndex(HeightMapData.AssetName)); HeightMapData.WriteTo(writer); writer.Write(assetNames.GetOrCreateAssetIndex(BlendTileData.AssetName)); BlendTileData.WriteTo(writer); writer.Write(assetNames.GetOrCreateAssetIndex(WorldInfo.AssetName)); WorldInfo.WriteTo(writer, assetNames); writer.Write(assetNames.GetOrCreateAssetIndex(SidesList.AssetName)); SidesList.WriteTo(writer, assetNames); writer.Write(assetNames.GetOrCreateAssetIndex(ObjectsList.AssetName)); ObjectsList.WriteTo(writer, assetNames); writer.Write(assetNames.GetOrCreateAssetIndex(PolygonTriggers.AssetName)); PolygonTriggers.WriteTo(writer); writer.Write(assetNames.GetOrCreateAssetIndex(GlobalLighting.AssetName)); GlobalLighting.WriteTo(writer); writer.Write(assetNames.GetOrCreateAssetIndex(WaypointsList.AssetName)); WaypointsList.WriteTo(writer); }
private static MapFile ParseMapData(BinaryReader reader) { var assetNames = AssetNameCollection.Parse(reader); var result = new MapFile(); var context = new MapParseContext(assetNames); context.PushAsset(nameof(MapFile), reader.BaseStream.Length); Asset.ParseAssets(reader, context, assetName => { switch (assetName) { case HeightMapData.AssetName: result.HeightMapData = HeightMapData.Parse(reader, context); break; case BlendTileData.AssetName: result.BlendTileData = BlendTileData.Parse(reader, context, result.HeightMapData); break; case WorldInfo.AssetName: result.WorldInfo = WorldInfo.Parse(reader, context); break; case SidesList.AssetName: result.SidesList = SidesList.Parse(reader, context); break; case ObjectsList.AssetName: result.ObjectsList = ObjectsList.Parse(reader, context); break; case PolygonTriggers.AssetName: result.PolygonTriggers = PolygonTriggers.Parse(reader, context); break; case GlobalLighting.AssetName: result.GlobalLighting = GlobalLighting.Parse(reader, context); break; case WaypointsList.AssetName: result.WaypointsList = WaypointsList.Parse(reader, context); break; default: throw new NotImplementedException(assetName); } }); context.PopAsset(); return(result); }
internal static HeightMapData Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var result = new HeightMapData { Width = reader.ReadUInt32(), Height = reader.ReadUInt32(), BorderWidth = reader.ReadUInt32() }; var borderCount = reader.ReadUInt32(); result.Borders = new HeightMapBorder[borderCount]; for (var i = 0; i < borderCount; i++) { result.Borders[i] = HeightMapBorder.Parse(reader, version); } result.Area = reader.ReadUInt32(); if (result.Width * result.Height != result.Area) { throw new InvalidDataException(); } result.Elevations = new ushort[result.Width, result.Height]; for (var y = 0; y < result.Height; y++) { for (var x = 0; x < result.Width; x++) { result.Elevations[x, y] = version >= 5 ? reader.ReadUInt16() : reader.ReadByte(); } } return result; })); }
private void WriteMapDataTo(BinaryWriter writer, AssetNameCollection assetNames) { if (AssetList != null) { writer.Write(assetNames.GetOrCreateAssetIndex(AssetList.AssetName)); AssetList.WriteTo(writer); } if (GlobalVersion != null) { writer.Write(assetNames.GetOrCreateAssetIndex(GlobalVersion.AssetName)); GlobalVersion.WriteTo(writer); } writer.Write(assetNames.GetOrCreateAssetIndex(HeightMapData.AssetName)); HeightMapData.WriteTo(writer); writer.Write(assetNames.GetOrCreateAssetIndex(BlendTileData.AssetName)); BlendTileData.WriteTo(writer); writer.Write(assetNames.GetOrCreateAssetIndex(WorldInfo.AssetName)); WorldInfo.WriteTo(writer, assetNames); if (MPPositionList != null) { writer.Write(assetNames.GetOrCreateAssetIndex(MPPositionList.AssetName)); MPPositionList.WriteTo(writer, assetNames); } writer.Write(assetNames.GetOrCreateAssetIndex(SidesList.AssetName)); SidesList.WriteTo(writer, assetNames, AssetList != null); if (LibraryMapLists != null) { writer.Write(assetNames.GetOrCreateAssetIndex(LibraryMapLists.AssetName)); LibraryMapLists.WriteTo(writer, assetNames); } if (Teams != null) { writer.Write(assetNames.GetOrCreateAssetIndex(Teams.AssetName)); Teams.WriteTo(writer, assetNames); } if (PlayerScriptsList != null) { writer.Write(assetNames.GetOrCreateAssetIndex(PlayerScriptsList.AssetName)); PlayerScriptsList.WriteTo(writer, assetNames); } if (BuildLists != null) { writer.Write(assetNames.GetOrCreateAssetIndex(BuildLists.AssetName)); BuildLists.WriteTo(writer, assetNames, AssetList != null); } writer.Write(assetNames.GetOrCreateAssetIndex(ObjectsList.AssetName)); ObjectsList.WriteTo(writer, assetNames); if (PolygonTriggers != null) { writer.Write(assetNames.GetOrCreateAssetIndex(PolygonTriggers.AssetName)); PolygonTriggers.WriteTo(writer); } if (TriggerAreas != null) { writer.Write(assetNames.GetOrCreateAssetIndex(TriggerAreas.AssetName)); TriggerAreas.WriteTo(writer); } if (GlobalWaterSettings != null) { writer.Write(assetNames.GetOrCreateAssetIndex(GlobalWaterSettings.AssetName)); GlobalWaterSettings.WriteTo(writer); } if (FogSettings != null) { writer.Write(assetNames.GetOrCreateAssetIndex(FogSettings.AssetName)); FogSettings.WriteTo(writer); } if (MissionHotSpots != null) { writer.Write(assetNames.GetOrCreateAssetIndex(MissionHotSpots.AssetName)); MissionHotSpots.WriteTo(writer); } if (MissionObjectives != null) { writer.Write(assetNames.GetOrCreateAssetIndex(MissionObjectives.AssetName)); MissionObjectives.WriteTo(writer); } if (StandingWaterAreas != null) { writer.Write(assetNames.GetOrCreateAssetIndex(StandingWaterAreas.AssetName)); StandingWaterAreas.WriteTo(writer); } if (RiverAreas != null) { writer.Write(assetNames.GetOrCreateAssetIndex(RiverAreas.AssetName)); RiverAreas.WriteTo(writer); } if (StandingWaveAreas != null) { writer.Write(assetNames.GetOrCreateAssetIndex(StandingWaveAreas.AssetName)); StandingWaveAreas.WriteTo(writer); } writer.Write(assetNames.GetOrCreateAssetIndex(GlobalLighting.AssetName)); GlobalLighting.WriteTo(writer); if (PostEffectsChunk != null) { writer.Write(assetNames.GetOrCreateAssetIndex(PostEffectsChunk.AssetName)); PostEffectsChunk.WriteTo(writer); } if (EnvironmentData != null) { writer.Write(assetNames.GetOrCreateAssetIndex(EnvironmentData.AssetName)); EnvironmentData.WriteTo(writer); } if (NamedCameras != null) { writer.Write(assetNames.GetOrCreateAssetIndex(NamedCameras.AssetName)); NamedCameras.WriteTo(writer); } if (CameraAnimationList != null) { writer.Write(assetNames.GetOrCreateAssetIndex(CameraAnimationList.AssetName)); CameraAnimationList.WriteTo(writer); } if (CastleTemplates != null) { writer.Write(assetNames.GetOrCreateAssetIndex(CastleTemplates.AssetName)); CastleTemplates.WriteTo(writer, assetNames); } writer.Write(assetNames.GetOrCreateAssetIndex(WaypointsList.AssetName)); WaypointsList.WriteTo(writer); if (SkyboxSettings != null) { writer.Write(assetNames.GetOrCreateAssetIndex(SkyboxSettings.AssetName)); SkyboxSettings.WriteTo(writer); } }
private static MapFile ParseMapData(BinaryReader reader) { var assetNames = AssetNameCollection.Parse(reader); var result = new MapFile(); var context = new MapParseContext(assetNames); context.PushAsset(nameof(MapFile), reader.BaseStream.Length); Asset.ParseAssets(reader, context, assetName => { switch (assetName) { case AssetList.AssetName: result.AssetList = AssetList.Parse(reader, context); break; case GlobalVersion.AssetName: result.GlobalVersion = GlobalVersion.Parse(reader, context); break; case HeightMapData.AssetName: result.HeightMapData = HeightMapData.Parse(reader, context); break; case BlendTileData.AssetName: result.BlendTileData = BlendTileData.Parse(reader, context, result.HeightMapData); break; case WorldInfo.AssetName: result.WorldInfo = WorldInfo.Parse(reader, context); break; case MPPositionList.AssetName: result.MPPositionList = MPPositionList.Parse(reader, context); break; case SidesList.AssetName: result.SidesList = SidesList.Parse(reader, context, result.AssetList != null); break; case LibraryMapLists.AssetName: result.LibraryMapLists = LibraryMapLists.Parse(reader, context); break; case Teams.AssetName: result.Teams = Teams.Parse(reader, context); break; case PlayerScriptsList.AssetName: result.PlayerScriptsList = PlayerScriptsList.Parse(reader, context); break; case BuildLists.AssetName: result.BuildLists = BuildLists.Parse(reader, context, result.AssetList != null); break; case ObjectsList.AssetName: result.ObjectsList = ObjectsList.Parse(reader, context); break; case PolygonTriggers.AssetName: result.PolygonTriggers = PolygonTriggers.Parse(reader, context); break; case TriggerAreas.AssetName: result.TriggerAreas = TriggerAreas.Parse(reader, context); break; case GlobalWaterSettings.AssetName: result.GlobalWaterSettings = GlobalWaterSettings.Parse(reader, context); break; case FogSettings.AssetName: result.FogSettings = FogSettings.Parse(reader, context); break; case MissionHotSpots.AssetName: result.MissionHotSpots = MissionHotSpots.Parse(reader, context); break; case MissionObjectives.AssetName: result.MissionObjectives = MissionObjectives.Parse(reader, context); break; case StandingWaterAreas.AssetName: result.StandingWaterAreas = StandingWaterAreas.Parse(reader, context); break; case RiverAreas.AssetName: result.RiverAreas = RiverAreas.Parse(reader, context); break; case StandingWaveAreas.AssetName: result.StandingWaveAreas = StandingWaveAreas.Parse(reader, context); break; case GlobalLighting.AssetName: result.GlobalLighting = GlobalLighting.Parse(reader, context); break; case PostEffectsChunk.AssetName: result.PostEffectsChunk = PostEffectsChunk.Parse(reader, context); break; case EnvironmentData.AssetName: result.EnvironmentData = EnvironmentData.Parse(reader, context); break; case NamedCameras.AssetName: result.NamedCameras = NamedCameras.Parse(reader, context); break; case CameraAnimationList.AssetName: result.CameraAnimationList = CameraAnimationList.Parse(reader, context); break; case CastleTemplates.AssetName: result.CastleTemplates = CastleTemplates.Parse(reader, context); break; case WaypointsList.AssetName: result.WaypointsList = WaypointsList.Parse(reader, context); break; case SkyboxSettings.AssetName: result.SkyboxSettings = SkyboxSettings.Parse(reader, context); break; default: throw new NotImplementedException(assetName); } }); context.PopAsset(); return(result); }
internal static BlendTileData Parse(BinaryReader reader, MapParseContext context, HeightMapData heightMapData) { return(ParseAsset(reader, context, version => { if (version < 6) { throw new InvalidDataException(); } if (heightMapData == null) { throw new InvalidDataException("Expected HeightMapData asset before BlendTileData asset."); } var width = heightMapData.Width; var height = heightMapData.Height; var result = new BlendTileData(); result.NumTiles = reader.ReadUInt32(); if (result.NumTiles != width * height) { throw new InvalidDataException(); } result.Tiles = reader.ReadUInt16Array2D(width, height); var blendBitSize = GetBlendBitSize(version); result.Blends = reader.ReadUIntArray2D(width, height, blendBitSize); result.ThreeWayBlends = reader.ReadUIntArray2D(width, height, blendBitSize); result.CliffTextures = reader.ReadUIntArray2D(width, height, blendBitSize); if (version > 6) { var passabilityWidth = heightMapData.Width; if (version == 7) { // C&C Generals clips partial bytes from each row of passability data, passabilityWidth = ((passabilityWidth + 1) / 8) * 8; } // If terrain is passable, there's a 0 in the data file. result.Impassability = reader.ReadSingleBitBooleanArray2D(passabilityWidth, heightMapData.Height); } if (version >= 10) { result.ImpassabilityToPlayers = reader.ReadSingleBitBooleanArray2D(heightMapData.Width, heightMapData.Height); } if (version >= 11) { result.PassageWidths = reader.ReadSingleBitBooleanArray2D(heightMapData.Width, heightMapData.Height); } if (version >= 14 && version < 25) { result.Taintability = reader.ReadSingleBitBooleanArray2D(heightMapData.Width, heightMapData.Height); } if (version >= 15) { result.ExtraPassability = reader.ReadSingleBitBooleanArray2D(heightMapData.Width, heightMapData.Height); } if (version >= 16 && version < 25) { result.Flammability = reader.ReadByteArray2DAsEnum <TileFlammability>(heightMapData.Width, heightMapData.Height); } if (version >= 17) { result.Visibility = reader.ReadSingleBitBooleanArray2D(heightMapData.Width, heightMapData.Height); } if (version >= 24) { // TODO: Are these in the right order? result.Buildability = reader.ReadSingleBitBooleanArray2D(heightMapData.Width, heightMapData.Height); result.ImpassabilityToAirUnits = reader.ReadSingleBitBooleanArray2D(heightMapData.Width, heightMapData.Height); result.TiberiumGrowability = reader.ReadSingleBitBooleanArray2D(heightMapData.Width, heightMapData.Height); } if (version >= 25) { result.DynamicShrubberyDensity = reader.ReadByteArray2D(heightMapData.Width, heightMapData.Height); } result.TextureCellCount = reader.ReadUInt32(); var blendsCount = reader.ReadUInt32(); if (blendsCount > 0) { // Usually minimum value is 1, but some files (perhaps Generals, not Zero Hour?) have 0. blendsCount--; } result.ParsedCliffTextureMappingsCount = reader.ReadUInt32(); var cliffBlendsCount = result.ParsedCliffTextureMappingsCount; if (cliffBlendsCount > 0) { // Usually minimum value is 1, but some files (perhaps Generals, not Zero Hour?) have 0. cliffBlendsCount--; } var textureCount = reader.ReadUInt32(); result.Textures = new BlendTileTexture[textureCount]; for (var i = 0; i < textureCount; i++) { result.Textures[i] = BlendTileTexture.Parse(reader); } // Can be a variety of values, don't know what it means. result.MagicValue1 = reader.ReadUInt32(); result.MagicValue2 = reader.ReadUInt32(); if (result.MagicValue2 != 0) { throw new InvalidDataException(); } result.BlendDescriptions = new BlendDescription[blendsCount]; for (var i = 0; i < blendsCount; i++) { result.BlendDescriptions[i] = BlendDescription.Parse(reader, version); } result.CliffTextureMappings = new CliffTextureMapping[cliffBlendsCount]; for (var i = 0; i < cliffBlendsCount; i++) { result.CliffTextureMappings[i] = CliffTextureMapping.Parse(reader); } return result; })); }
internal static BlendTileData Parse(BinaryReader reader, MapParseContext context, HeightMapData heightMapData) { return(ParseAsset(reader, context, version => { if (version < 6 || version > 8) { throw new InvalidDataException(); } if (heightMapData == null) { throw new InvalidDataException("Expected HeightMapData asset before BlendTileData asset."); } var width = heightMapData.Width; var height = heightMapData.Height; var numTiles = reader.ReadUInt32(); if (numTiles != width * height) { throw new InvalidDataException(); } var tiles = reader.ReadUInt16Array2D(width, height); var blends = reader.ReadUInt16Array2D(width, height); var threeWayBlends = reader.ReadUInt16Array2D(width, height); var cliffTextures = reader.ReadUInt16Array2D(width, height); bool[,] passability = null; if (version > 6) { var passabilityWidth = heightMapData.Width; if (version == 7) { // C&C Generals clips partial bytes from each row of passability data, // if the border width is large enough to fully contain the clipped data. if (passabilityWidth % 8 <= 6 && passabilityWidth % 8 <= heightMapData.BorderWidth) { passabilityWidth -= passabilityWidth % 8; } } // If terrain is passable, there's a 0 in the data file. passability = reader.ReadSingleBitBooleanArray2D(passabilityWidth, heightMapData.Height); } var textureCellCount = reader.ReadUInt32(); var blendsCount = reader.ReadUInt32(); if (blendsCount > 0) { // Usually minimum value is 1, but some files (perhaps Generals, not Zero Hour?) have 0. blendsCount--; } var parsedCliffBlendsCount = reader.ReadUInt32(); var cliffBlendsCount = parsedCliffBlendsCount; if (cliffBlendsCount > 0) { // Usually minimum value is 1, but some files (perhaps Generals, not Zero Hour?) have 0. cliffBlendsCount--; } var textureCount = reader.ReadUInt32(); var textures = new BlendTileTexture[textureCount]; for (var i = 0; i < textureCount; i++) { textures[i] = BlendTileTexture.Parse(reader); } var magicValue1 = reader.ReadUInt32(); if (magicValue1 != 0) { throw new InvalidDataException(); } var magicValue2 = reader.ReadUInt32(); if (magicValue2 != 0) { throw new InvalidDataException(); } var blendDescriptions = new BlendDescription[blendsCount]; for (var i = 0; i < blendsCount; i++) { blendDescriptions[i] = BlendDescription.Parse(reader); } var cliffTextureMappings = new CliffTextureMapping[cliffBlendsCount]; for (var i = 0; i < cliffBlendsCount; i++) { cliffTextureMappings[i] = CliffTextureMapping.Parse(reader); } return new BlendTileData { NumTiles = numTiles, Tiles = tiles, Blends = blends, ThreeWayBlends = threeWayBlends, CliffTextures = cliffTextures, Passability = passability, TextureCellCount = textureCellCount, Textures = textures, MagicValue1 = magicValue1, MagicValue2 = magicValue2, BlendDescriptions = blendDescriptions, ParsedCliffTextureMappingsCount = parsedCliffBlendsCount, CliffTextureMappings = cliffTextureMappings }; })); }