internal static T ParseAsset <T>(BinaryReader reader, MapParseContext context, AssetParseCallback <T> parseCallback) where T : Asset { var assetVersion = reader.ReadUInt16(); var dataSize = reader.ReadUInt32(); var startPosition = reader.BaseStream.Position; var endPosition = dataSize + startPosition; context.PushAsset(typeof(T).Name, endPosition); var result = parseCallback(assetVersion); result.StartPosition = startPosition; result.EndPosition = endPosition; result.Version = assetVersion; context.PopAsset(); if (reader.BaseStream.Position != endPosition) { throw new InvalidDataException($"Error while parsing asset '{typeof(T).Name}', version {assetVersion}. Expected reader to be at position {endPosition}, but was at {reader.BaseStream.Position}."); } return(result); }
internal static ScriptOrCondition Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var conditions = new List <ScriptCondition>(); ParseAssets(reader, context, assetName => { switch (assetName) { case ScriptCondition.AssetName: conditions.Add(ScriptCondition.Parse(reader, context)); break; default: throw new InvalidDataException($"Unexpected asset: {assetName}"); } }); return new ScriptOrCondition { Conditions = conditions.ToArray() }; })); }
internal static EnvironmentData Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, (version, endPosition) => { var result = new EnvironmentData(); if (version >= 3) { result.WaterMaxAlphaDepth = reader.ReadSingle(); result.DeepWaterAlpha = reader.ReadSingle(); } if (version < 5) { result.Unknown = reader.ReadBooleanChecked(); } result.MacroTexture = reader.ReadUInt16PrefixedAsciiString(); result.CloudTexture = reader.ReadUInt16PrefixedAsciiString(); if (version >= 4) { result.UnknownTexture = reader.ReadUInt16PrefixedAsciiString(); } // Both RA3 Uprising and C&C4 used v6 for this chunk, but RA3 Uprising had an extra texture here. if (version >= 6 && reader.BaseStream.Position < endPosition) { result.UnknownTexture2 = reader.ReadUInt16PrefixedAsciiString(); } return result; })); }
internal static EnvironmentData Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var result = new EnvironmentData(); if (version >= 3) { result.WaterMaxAlphaDepth = reader.ReadSingle(); result.DeepWaterAlpha = reader.ReadSingle(); } result.Unknown = reader.ReadBooleanChecked(); result.MacroTexture = reader.ReadUInt16PrefixedAsciiString(); result.CloudTexture = reader.ReadUInt16PrefixedAsciiString(); if (version >= 4) { result.UnknownTexture = reader.ReadUInt16PrefixedAsciiString(); } return result; })); }
internal static CastleTemplates Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var propertyKey = AssetPropertyKey.Parse(reader, context); var count = reader.ReadUInt32(); var result = new CastleTemplate[count]; for (var i = 0; i < count; i++) { result[i] = CastleTemplate.Parse(reader, version); } CastlePerimeter perimeter = null; if (version >= 2) { perimeter = CastlePerimeter.Parse(reader, version); } return new CastleTemplates { PropertyKey = propertyKey, Templates = result, Perimeter = perimeter }; })); }
internal static MPPositionInfo Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var result = new MPPositionInfo { IsHuman = reader.ReadBooleanChecked(), IsComputer = reader.ReadBooleanChecked() }; if (version > 0) { // I'm guessing about what 5 bytes are missing in version 0, compared to version 1. result.LoadAIScript = reader.ReadBooleanChecked(); } result.Team = reader.ReadUInt32(); if (version > 0) { result.SideRestrictions = new string[reader.ReadUInt32()]; for (var i = 0; i < result.SideRestrictions.Length; i++) { result.SideRestrictions[i] = reader.ReadUInt16PrefixedAsciiString(); } } return result; })); }
internal static Team Parse(BinaryReader reader, MapParseContext context) { return(new Team { Properties = AssetPropertyCollection.Parse(reader, context) }); }
internal static BuildList Parse(BinaryReader reader, MapParseContext context, ushort version, bool mapHasAssetList) { var result = new BuildList(); // BFME and C&C3 both used v1 for this chunk, but store the faction name differently :( // If the map file has an AssetList chunk, we assume it's C&C3. if (mapHasAssetList) { result.FactionName = reader.ReadUInt16PrefixedAsciiString(); } else { result.FactionNameProperty = AssetPropertyKey.Parse(reader, context); } var numBuildListItems = reader.ReadUInt32(); result.Items = new BuildListItem[numBuildListItems]; for (var i = 0; i < numBuildListItems; i++) { result.Items[i] = BuildListItem.Parse(reader, version, 1, mapHasAssetList); } return(result); }
internal static SkyboxSettings Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => new SkyboxSettings { Position = reader.ReadVector3(), Scale = reader.ReadSingle(), Rotation = reader.ReadSingle(), TextureScheme = reader.ReadUInt16PrefixedAsciiString() })); }
internal static WorldInfo Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { return new WorldInfo { Properties = AssetPropertyCollection.Parse(reader, context) }; })); }
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 GlobalWaterSettings Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { return new GlobalWaterSettings { ReflectionOn = reader.ReadBooleanUInt32Checked(), ReflectionPlaneZ = reader.ReadSingle() }; })); }
internal static SkyboxSettings Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { // TODO return new SkyboxSettings { Unknown = reader.ReadBytes(32) }; })); }
internal static void ParseAssets(BinaryReader reader, MapParseContext context, AssetsParseCallback parseCallback) { while (reader.BaseStream.Position < context.CurrentEndPosition) { var assetIndex = reader.ReadUInt32(); var assetName = context.GetAssetName(assetIndex); parseCallback(assetName); } }
internal static AssetPropertyCollection Parse(BinaryReader reader, MapParseContext context) { var numProperties = reader.ReadUInt16(); var result = new AssetProperty[numProperties]; for (var i = 0; i < numProperties; i++) { result[i] = AssetProperty.Parse(reader, context); } return(new AssetPropertyCollection(result)); }
internal static FogSettings Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var result = new FogSettings(); // TODO result.Unknown = reader.ReadBytes(24); return result; })); }
internal static AssetPropertyKey Parse(BinaryReader reader, MapParseContext context) { var propertyType = reader.ReadByteAsEnum <AssetPropertyType>(); var propertyNameIndex = reader.ReadUInt24(); var propertyName = context.GetAssetName(propertyNameIndex); return(new AssetPropertyKey { PropertyType = propertyType, Name = propertyName }); }
internal static MapObject Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { return new MapObject { Position = reader.ReadVector3(), Angle = reader.ReadSingle(), RoadType = reader.ReadUInt32AsEnumFlags <RoadType>(), TypeName = reader.ReadUInt16PrefixedAsciiString(), Properties = AssetPropertyCollection.Parse(reader, context) }; })); }
internal static ScriptCondition Parse(BinaryReader reader, MapParseContext context) { return(Parse( reader, context, MinimumVersionThatHasInternalName, MinimumVersionThatHasEnabledFlag, (version, x) => { if (version >= MinimumVersionThatHasEnabledFlag) { x.IsInverted = reader.ReadBooleanUInt32Checked(); } })); }
internal static GlobalLighting Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var time = reader.ReadUInt32AsEnum <TimeOfDay>(); var lightingConfigurations = new Dictionary <TimeOfDay, GlobalLightingConfiguration>(); for (var i = 0; i < TimeOfDayValues.Length; i++) { lightingConfigurations[TimeOfDayValues[i]] = GlobalLightingConfiguration.Parse(reader, version); } var shadowColor = MapColorArgb.Parse(reader); // TODO: BFME. Overbright? Bloom? byte[] unknown = null; if (version >= 7 && version < 11) { unknown = reader.ReadBytes(version >= 9 ? 4 : 44); } Vector3?unknown2 = null; MapColorArgb?unknown3 = null; if (version >= 12) { unknown2 = reader.ReadVector3(); unknown3 = MapColorArgb.Parse(reader); } ColorRgbF?noCloudFactor = null; if (version >= 8) { noCloudFactor = reader.ReadColorRgbF(); } return new GlobalLighting { Time = time, LightingConfigurations = lightingConfigurations, ShadowColor = shadowColor, Unknown = unknown, Unknown2 = unknown2, Unknown3 = unknown3, NoCloudFactor = noCloudFactor }; })); }
internal static LibraryMaps Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var values = new string[reader.ReadUInt32()]; for (var i = 0; i < values.Length; i++) { values[i] = reader.ReadUInt16PrefixedAsciiString(); } return new LibraryMaps { Values = values }; })); }
internal static HeightMapData Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var mapWidth = reader.ReadUInt32(); var mapHeight = reader.ReadUInt32(); var borderWidth = reader.ReadUInt32(); var perimeterCount = reader.ReadUInt32(); var perimeters = new HeightMapPerimeter[perimeterCount]; for (var i = 0; i < perimeterCount; i++) { perimeters[i] = new HeightMapPerimeter { Width = reader.ReadUInt32(), Height = reader.ReadUInt32() }; } var area = reader.ReadUInt32(); if (mapWidth * mapHeight != area) { throw new InvalidDataException(); } var elevations = new byte[mapWidth, mapHeight]; for (var y = 0; y < mapHeight; y++) { for (var x = 0; x < mapWidth; x++) { elevations[x, y] = reader.ReadByte(); } } return new HeightMapData { Width = mapWidth, Height = mapHeight, BorderWidth = borderWidth, Perimeters = perimeters, Area = area, Elevations = elevations }; })); }
internal static SidesList Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var numPlayers = reader.ReadUInt32(); var players = new Player[numPlayers]; for (var i = 0; i < numPlayers; i++) { players[i] = Player.Parse(reader, context); } var numTeams = reader.ReadUInt32(); var teams = new Team[numTeams]; for (var i = 0; i < numTeams; i++) { teams[i] = Team.Parse(reader, context); } PlayerScriptsList playerScripts = null; ParseAssets(reader, context, assetName => { if (assetName != PlayerScriptsList.AssetName) { throw new InvalidDataException(); } if (playerScripts != null) { throw new InvalidDataException(); } playerScripts = PlayerScriptsList.Parse(reader, context); }); return new SidesList { Players = players, Teams = teams, PlayerScripts = playerScripts }; })); }
internal static AssetList Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var numAssets = reader.ReadUInt32(); var items = new AssetListItem[numAssets]; for (var i = 0; i < numAssets; i++) { items[i] = AssetListItem.Parse(reader); } return new AssetList { Items = items }; })); }
internal static TriggerAreas Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var numTriggers = reader.ReadUInt32(); var triggers = new TriggerArea[numTriggers]; for (var i = 0; i < numTriggers; i++) { triggers[i] = TriggerArea.Parse(reader); } return new TriggerAreas { Areas = triggers }; })); }
internal static WaypointsList Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var numWaypointPaths = reader.ReadUInt32(); var waypointPaths = new WaypointPath[numWaypointPaths]; for (var i = 0; i < numWaypointPaths; i++) { waypointPaths[i] = WaypointPath.Parse(reader); } return new WaypointsList { WaypointPaths = waypointPaths }; })); }
internal static StandingWaveAreas Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var numAreas = reader.ReadUInt32(); var areas = new StandingWaveArea[numAreas]; for (var i = 0; i < numAreas; i++) { areas[i] = StandingWaveArea.Parse(reader, version); } return new StandingWaveAreas { Areas = areas }; })); }
internal static NamedCameras Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var numNamedCameras = reader.ReadUInt32(); var cameras = new NamedCamera[numNamedCameras]; for (var i = 0; i < numNamedCameras; i++) { cameras[i] = NamedCamera.Parse(reader); } return new NamedCameras { Cameras = cameras }; })); }
internal static BuildLists Parse(BinaryReader reader, MapParseContext context, bool mapHasAssetList) { return(ParseAsset(reader, context, version => { var numBuildLists = reader.ReadUInt32(); var buildLists = new BuildList[numBuildLists]; for (var i = 0; i < numBuildLists; i++) { buildLists[i] = BuildList.Parse(reader, context, version, mapHasAssetList); } return new BuildLists { Items = buildLists }; })); }
internal static MissionObjectives Parse(BinaryReader reader, MapParseContext context) { return(ParseAsset(reader, context, version => { var numItems = reader.ReadUInt32(); var items = new MissionObjective[numItems]; for (var i = 0; i < numItems; i++) { items[i] = MissionObjective.Parse(reader); } return new MissionObjectives { Items = items }; })); }