Пример #1
0
        public void IcePalace_ReadTest()
        {
            TR2LevelReader reader = new TR2LevelReader();

            TR2Level lvl = reader.ReadLevel("icecave.tr2");

            byte[] lvlAsBytes = File.ReadAllBytes("icecave.tr2");

            //Does our view of the level match byte for byte?
            CollectionAssert.AreEqual(lvlAsBytes, lvl.Serialize(), "Read does not match byte for byte");

            TR2LevelWriter writer = new TR2LevelWriter();

            writer.WriteLevelToFile(lvl, "TEST.tr2");

            byte[] copyAsBytes = File.ReadAllBytes("TEST.tr2");

            //Does our saved copy match the original?
            CollectionAssert.AreEqual(lvlAsBytes, copyAsBytes, "Write does not match byte for byte");
        }
Пример #2
0
        public void CalculateDependencies(TR2Level level, TR2Entities entity)
        {
            using (TexturePacker packer = new TexturePacker(level))
            {
                Dictionary <TexturedTile, List <TexturedTileSegment> > entitySegments = packer.GetModelSegments(entity);
                foreach (TRModel model in level.Models)
                {
                    TR2Entities otherEntity = (TR2Entities)model.ID;
                    if (entity == otherEntity)
                    {
                        continue;
                    }

                    Dictionary <TexturedTile, List <TexturedTileSegment> > modelSegments = packer.GetModelSegments(otherEntity);

                    foreach (TexturedTile tile in entitySegments.Keys)
                    {
                        if (modelSegments.ContainsKey(tile))
                        {
                            List <TexturedTileSegment> matches = entitySegments[tile].FindAll(s1 => modelSegments[tile].Any(s2 => s1 == s2));
                            foreach (TexturedTileSegment matchedSegment in matches)
                            {
                                TextureDependency dependency = GetDependency(tile.Index, matchedSegment.Bounds);
                                if (dependency == null)
                                {
                                    dependency = new TextureDependency
                                    {
                                        TileIndex = tile.Index,
                                        Bounds    = matchedSegment.Bounds
                                    };
                                    Dependencies.Add(dependency);
                                }
                                dependency.AddEntity(entity);
                                dependency.AddEntity(otherEntity);
                                System.Diagnostics.Debug.WriteLine("\t\t" + otherEntity + ", " + tile.Index + ": " + matchedSegment.Bounds);
                            }
                        }
                    }
                }
            }
        }
Пример #3
0
        /// <summary>
        /// Duplicates the data from one mesh to another and ensures that the contents
        /// of MeshPointers remains consistent with respect to the mesh lengths.
        /// </summary>
        public static void DuplicateMesh(TR2Level level, TRMesh originalMesh, TRMesh replacementMesh)
        {
            int oldLength = originalMesh.Serialize().Length;

            originalMesh.Centre                = replacementMesh.Centre;
            originalMesh.CollRadius            = replacementMesh.CollRadius;
            originalMesh.ColouredRectangles    = replacementMesh.ColouredRectangles;
            originalMesh.ColouredTriangles     = replacementMesh.ColouredTriangles;
            originalMesh.Lights                = replacementMesh.Lights;
            originalMesh.Normals               = replacementMesh.Normals;
            originalMesh.NumColouredRectangles = replacementMesh.NumColouredRectangles;
            originalMesh.NumColouredTriangles  = replacementMesh.NumColouredTriangles;
            originalMesh.NumNormals            = replacementMesh.NumNormals;
            originalMesh.NumTexturedRectangles = replacementMesh.NumTexturedRectangles;
            originalMesh.NumTexturedTriangles  = replacementMesh.NumTexturedTriangles;
            originalMesh.NumVertices           = replacementMesh.NumVertices;
            originalMesh.TexturedRectangles    = replacementMesh.TexturedRectangles;
            originalMesh.TexturedTriangles     = replacementMesh.TexturedTriangles;
            originalMesh.Vertices              = replacementMesh.Vertices;

            // The length will have changed so all pointers above the original one will need adjusting
            int         lengthDiff   = originalMesh.Serialize().Length - oldLength;
            List <uint> pointers     = level.MeshPointers.ToList();
            int         pointerIndex = pointers.IndexOf(originalMesh.Pointer);

            for (int i = pointerIndex + 1; i < pointers.Count; i++)
            {
                if (pointers[i] > 0)
                {
                    int newPointer = (int)pointers[i] + lengthDiff;
                    pointers[i] = (uint)newPointer;
                }
            }

            level.MeshPointers = pointers.ToArray();

            int numMeshData = (int)level.NumMeshData + lengthDiff / 2;

            level.NumMeshData = (uint)numMeshData;
        }
Пример #4
0
        public TexturePacker(TR2Level level, ITextureClassifier classifier = null)
        {
            TileWidth    = 256;
            TileHeight   = 256;
            MaximumTiles = 16;

            Options = new PackingOptions
            {
                FillMode    = PackingFillMode.Vertical,
                OrderMode   = PackingOrderMode.Height,
                Order       = PackingOrder.Descending,
                GroupMode   = PackingGroupMode.None,
                StartMethod = PackingStartMethod.EndTile
            };

            Level            = level;
            _levelClassifier = classifier == null ? string.Empty : classifier.GetClassification();

            _allTextures = new List <AbstractIndexedTRTexture>();

            if (Level != null)
            {
                _allTextures.AddRange(LoadObjectTextures());
                _allTextures.AddRange(LoadSpriteTextures());
                _allTextures.Sort(new TRTextureReverseAreaComparer());

                for (int i = 0; i < Level.NumImages; i++)
                {
                    TexturedTile tile = AddTile();
                    tile.BitmapGraphics   = GetTileBitmap(i);
                    tile.AllowOverlapping = true; // Allow initially for the likes of Opera House - see tile 3 [128, 128]
                }

                foreach (AbstractIndexedTRTexture texture in _allTextures)
                {
                    _tiles[texture.Atlas].AddTexture(texture);
                }
            }
        }
Пример #5
0
        private int GetModelAnimationCount(TR2Level level, TRModel model)
        {
            TRModel nextModel  = model;
            int     modelIndex = level.Models.ToList().IndexOf(model) + 1;

            while (modelIndex < level.NumModels)
            {
                nextModel = level.Models[modelIndex++];
                if (nextModel.Animation != ushort.MaxValue)
                {
                    break;
                }
            }

            ushort nextStartAnimation = nextModel.Animation;

            if (model == nextModel)
            {
                nextStartAnimation = (ushort)level.NumAnimations;
            }

            return(model.Animation == ushort.MaxValue ? 0 : nextStartAnimation - model.Animation);
        }
Пример #6
0
        public void FloorData_AppendFDActionListItemCamTest()
        {
            //Read Dragons Lair data
            TR2LevelReader reader = new TR2LevelReader();
            TR2Level       lvl    = reader.ReadLevel("xian.tr2");

            //Parse the floordata using FDControl
            FDControl fdataReader = new FDControl();

            fdataReader.ParseFromLevel(lvl);

            //Add a music action to the trigger at index 6010
            //This has a CamAction in its TrigList so this tests
            //that the Continue flag is correctly set
            FDTriggerEntry trigger = fdataReader.Entries[6010][1] as FDTriggerEntry;

            Assert.AreEqual(trigger.TrigActionList.Count, 2);
            Assert.IsNotNull(trigger.TrigActionList[1].CamAction);
            Assert.IsFalse(trigger.TrigActionList[1].CamAction.Continue);

            trigger.TrigActionList.Add(new FDActionListItem
            {
                TrigAction = FDTrigAction.PlaySoundtrack,
                Parameter  = 40
            });

            //Write the data back
            fdataReader.WriteToLevel(lvl);

            //Check the CamAction has been updated
            Assert.AreEqual(trigger.TrigActionList.Count, 3);
            Assert.IsNotNull(trigger.TrigActionList[1].CamAction);
            Assert.IsTrue(trigger.TrigActionList[1].CamAction.Continue);

            //Check the music trigger has Continue set to false
            Assert.IsFalse(trigger.TrigActionList[2].Continue);
        }
Пример #7
0
        private void RandomizeSecretTracks(TR2Level level, FDControl floorData)
        {
            // Generate new triggers for secrets to allow different sounds for each one
            List <TRAudioTrack>         secretTracks = _tracks[TRAudioCategory.Secret];
            Dictionary <int, TR2Entity> secrets      = GetSecretItems(level);

            foreach (int entityIndex in secrets.Keys)
            {
                TR2Entity    secret = secrets[entityIndex];
                TRRoomSector sector = FDUtilities.GetRoomSector(secret.X, secret.Y, secret.Z, secret.Room, level, floorData);
                if (sector.FDIndex == 0)
                {
                    // The secret is positioned on a tile that currently has no FD, so create it
                    floorData.CreateFloorData(sector);
                }

                List <FDEntry> entries = floorData.Entries[sector.FDIndex];
                FDTriggerEntry existingTriggerEntry = entries.Find(e => e is FDTriggerEntry) as FDTriggerEntry;
                bool           existingEntityPickup = false;
                if (existingTriggerEntry != null)
                {
                    if (existingTriggerEntry.TrigType == FDTrigType.Pickup && existingTriggerEntry.TrigActionList[0].Parameter == entityIndex)
                    {
                        // GW gold secret (default location) already has a pickup trigger to spawn the
                        // TRex (or whatever enemy) so we'll just append to that item list here
                        existingEntityPickup = true;
                    }
                    else
                    {
                        // There is already a non-pickup trigger for this sector so while nothing is wrong with
                        // adding a pickup trigger, the game ignores it. So in this instance, the sound that
                        // plays will just be whatever is set in the script.
                        continue;
                    }
                }

                // Generate a new music action
                FDActionListItem musicAction = new FDActionListItem
                {
                    TrigAction = FDTrigAction.PlaySoundtrack,
                    Parameter  = secretTracks[_generator.Next(0, secretTracks.Count)].ID
                };

                // For GW default gold, just append it
                if (existingEntityPickup)
                {
                    existingTriggerEntry.TrigActionList.Add(musicAction);
                }
                else
                {
                    entries.Add(new FDTriggerEntry
                    {
                        // The values here are replicated from Trigger#112 (in trview) in GW.
                        // The first action list must be the entity being picked up and so
                        // remaining action list items are actioned on pick up.
                        Setup = new FDSetup {
                            Value = 1028
                        },
                        TrigSetup = new FDTrigSetup {
                            Value = 15872
                        },
                        TrigActionList = new List <FDActionListItem>
                        {
                            new FDActionListItem
                            {
                                TrigAction = FDTrigAction.Object,
                                Parameter  = (ushort)entityIndex
                            },
                            musicAction
                        }
                    });
                }
            }
        }
Пример #8
0
        public void FloorData_InsertFDTest()
        {
            //Read Dragons Lair data
            TR2LevelReader reader = new TR2LevelReader();
            TR2Level       lvl    = reader.ReadLevel("xian.tr2");

            //Parse the floordata using FDControl
            FDControl fdataReader = new FDControl();

            fdataReader.ParseFromLevel(lvl);

            //Find a sector that currently has no floor data
            int room, roomSector = -1;

            for (room = 0; room < lvl.NumRooms; room++)
            {
                roomSector = lvl.Rooms[room].SectorList.ToList().FindIndex(s => s.FDIndex == 0);
                if (roomSector != -1)
                {
                    break;
                }
            }

            if (roomSector == -1)
            {
                Assert.Fail("Could not locate a Room Sector that does not have floor data associated with it.");
            }

            TRRoomSector sector = lvl.Rooms[room].SectorList[roomSector];

            // Create a slot in the FD for this sector
            fdataReader.CreateFloorData(sector);
            Assert.AreNotEqual(sector.FDIndex, 0, "Sector does not have FD allocated.");

            // Add a music trigger
            fdataReader.Entries[sector.FDIndex].Add(new FDTriggerEntry
            {
                Setup          = new FDSetup(FDFunctions.Trigger),
                TrigSetup      = new FDTrigSetup(),
                TrigActionList = new List <FDActionListItem>
                {
                    new FDActionListItem
                    {
                        TrigAction = FDTrigAction.PlaySoundtrack,
                        Parameter  = 40
                    }
                }
            });

            //Write the data back
            fdataReader.WriteToLevel(lvl);

            //Save it and read it back in
            TR2LevelWriter writer = new TR2LevelWriter();

            writer.WriteLevelToFile(lvl, "TEST.tr2");
            lvl = reader.ReadLevel("TEST.tr2");

            //Reassign the sector
            sector = lvl.Rooms[room].SectorList[roomSector];

            fdataReader = new FDControl();
            fdataReader.ParseFromLevel(lvl);

            //Ensure the sector still has FD associated with it
            Assert.AreNotEqual(sector.FDIndex, 0, "Sector no longer has FD after write/read.");

            //Verify there is one entry for this sector
            Assert.AreEqual(fdataReader.Entries[sector.FDIndex].Count, 1);

            //Verify the trigger we added matches what we expect
            FDEntry entry = fdataReader.Entries[sector.FDIndex][0];

            Assert.IsTrue(entry is FDTriggerEntry);

            FDTriggerEntry triggerEntry = entry as FDTriggerEntry;

            Assert.IsTrue(triggerEntry.Setup.Function == (byte)FDFunctions.Trigger);
            Assert.IsTrue(triggerEntry.TrigActionList.Count == 1);
            Assert.IsTrue(triggerEntry.TrigActionList[0].TrigAction == FDTrigAction.PlaySoundtrack);
            Assert.IsTrue(triggerEntry.TrigActionList[0].Parameter == 40);
        }
Пример #9
0
        public void FloorData_InsertRemoveFDEntryTest()
        {
            //Read Dragons Lair data
            TR2LevelReader reader = new TR2LevelReader();
            TR2Level       lvl    = reader.ReadLevel("xian.tr2");

            //Store the original floordata from the level
            ushort[] originalFData = new ushort[lvl.NumFloorData];
            Array.Copy(lvl.FloorData, originalFData, lvl.NumFloorData);

            //Parse the floordata using FDControl
            FDControl fdataReader = new FDControl();

            fdataReader.ParseFromLevel(lvl);

            //Verify index 9 has one entry and that it's currently
            //set as EndData for this index
            Assert.AreEqual(fdataReader.Entries[9].Count, 1);
            Assert.IsTrue(fdataReader.Entries[9][0].Setup.EndData);

            //Verify the next index is currently 9 + the entry's length
            List <int> indices   = fdataReader.Entries.Keys.ToList();
            int        nextIndex = 9 + fdataReader.Entries[9][0].Flatten().Length;

            Assert.AreEqual(nextIndex, indices[indices.IndexOf(9) + 1]);

            //Add a music trigger to index 9
            fdataReader.Entries[9].Add(new FDTriggerEntry
            {
                Setup          = new FDSetup(FDFunctions.Trigger),
                TrigSetup      = new FDTrigSetup(),
                TrigActionList = new List <FDActionListItem>
                {
                    new FDActionListItem
                    {
                        TrigAction = FDTrigAction.PlaySoundtrack,
                        Parameter  = 40
                    }
                }
            });

            //Write the data back
            fdataReader.WriteToLevel(lvl);

            //Verify index 9 has two entries, that its first entry
            //does not have EndData set, but that its second does
            Assert.AreEqual(fdataReader.Entries[9].Count, 2);
            Assert.IsFalse(fdataReader.Entries[9][0].Setup.EndData);
            Assert.IsTrue(fdataReader.Entries[9][1].Setup.EndData);

            //Verify the next index is now 9 + both the entry's lengths
            //Bear in mind the underlying dictionary's keys have changed
            indices   = fdataReader.Entries.Keys.ToList();
            nextIndex = 9 + fdataReader.Entries[9][0].Flatten().Length + fdataReader.Entries[9][1].Flatten().Length;
            Assert.AreEqual(nextIndex, indices[indices.IndexOf(9) + 1]);

            //Remove the new entry
            fdataReader.Entries[9].RemoveAt(1);

            //Write the data back
            fdataReader.WriteToLevel(lvl);

            //Verify index 9 again has one entry and that it's again
            //set as EndData for this index
            Assert.AreEqual(fdataReader.Entries[9].Count, 1);
            Assert.IsTrue(fdataReader.Entries[9][0].Setup.EndData);

            //Verify the next index is again 9 + the entry's length
            indices   = fdataReader.Entries.Keys.ToList();
            nextIndex = 9 + fdataReader.Entries[9][0].Flatten().Length;
            Assert.AreEqual(nextIndex, indices[indices.IndexOf(9) + 1]);

            //Finally compare to make sure the original fdata was written back.
            CollectionAssert.AreEqual(originalFData, lvl.FloorData, "Floordata does not match");
            Assert.AreEqual((uint)lvl.FloorData.Length, lvl.NumFloorData);
        }
Пример #10
0
        public void FloorData_ReadWriteTest()
        {
            //Read Dragons Lair data
            TR2LevelReader reader = new TR2LevelReader();
            TR2Level       lvl    = reader.ReadLevel("xian.tr2");

            //Store the original floordata from the level
            ushort[] originalFData = new ushort[lvl.NumFloorData];
            Array.Copy(lvl.FloorData, originalFData, lvl.NumFloorData);

            //Parse the floordata using FDControl and re-write the parsed data back
            FDControl fdataReader = new FDControl();

            fdataReader.ParseFromLevel(lvl);
            fdataReader.WriteToLevel(lvl);

            //Store the new floordata written back by FDControl
            ushort[] newFData = lvl.FloorData;

            //Compare to make sure the original fdata was written back.
            CollectionAssert.AreEqual(originalFData, newFData, "Floordata does not match");
            Assert.AreEqual((uint)newFData.Length, lvl.NumFloorData);

            //Now modify an entry, and ensure it is different to the original data.
            FDPortalEntry portal = fdataReader.Entries[3][0] as FDPortalEntry;

            portal.Room = 42;
            fdataReader.WriteToLevel(lvl);

            //Test. FDIndex 3 of Dragon's Lair is a portal to room 3, which is being modified to Room 42.
            //Data should be:
            //New - [3] = 0x8001 and [4] = 0x002A

            //Get ref to new data
            newFData = lvl.FloorData;

            Assert.AreEqual(newFData[3], (ushort)0x8001);
            Assert.AreEqual(newFData[4], (ushort)0x002A);

            //Compare to make sure the modified fdata was written back.
            CollectionAssert.AreNotEqual(originalFData, newFData, "Floordata matches, change unsuccessful");
            Assert.AreEqual((uint)newFData.Length, lvl.NumFloorData);

            //Test pattern/type matching example for fdata.
            bool isPortal = false;

            foreach (KeyValuePair <int, List <FDEntry> > sector in fdataReader.Entries)
            {
                foreach (FDEntry entry in sector.Value)
                {
                    switch (entry)
                    {
                    case FDClimbEntry climbEntry:
                        break;

                    case FDKillLaraEntry killEntry:
                        break;

                    case FDPortalEntry portalEntry:
                        isPortal = true;
                        break;

                    case FDSlantEntry slantEntry:
                        break;

                    case FDTriggerEntry triggerEntry:
                        break;
                    }
                }
            }

            Assert.IsTrue(isPortal);
        }
Пример #11
0
        public void FloorData_ReadWriteOneShotTest()
        {
            //Read GW data
            TR2LevelReader reader = new TR2LevelReader();
            TR2Level       lvl    = reader.ReadLevel("wall.tr2");

            //Parse the floordata using FDControl
            FDControl fdataReader = new FDControl();

            fdataReader.ParseFromLevel(lvl);

            //Get all triggers for entity ID 18
            List <FDTriggerEntry> triggers = FDUtilities.GetEntityTriggers(fdataReader, 18);

            //There should be 3
            Assert.AreEqual(triggers.Count, 3);

            //Verify none of the triggers has OneShot set
            foreach (FDTriggerEntry trigger in triggers)
            {
                Assert.IsFalse(trigger.TrigSetup.OneShot);
            }

            //Set OneShot on each trigger
            foreach (FDTriggerEntry trigger in triggers)
            {
                trigger.TrigSetup.SetOneShot();
            }

            fdataReader.WriteToLevel(lvl);

            //Save it and read it back in
            TR2LevelWriter writer = new TR2LevelWriter();

            writer.WriteLevelToFile(lvl, "TEST.tr2");
            lvl = reader.ReadLevel("TEST.tr2");

            fdataReader = new FDControl();
            fdataReader.ParseFromLevel(lvl);

            //Get the triggers again afresh
            triggers = FDUtilities.GetEntityTriggers(fdataReader, 18);

            //Verify that they now have OneShot set
            foreach (FDTriggerEntry trigger in triggers)
            {
                Assert.IsTrue(trigger.TrigSetup.OneShot);
            }

            //Switch it off again
            foreach (FDTriggerEntry trigger in triggers)
            {
                trigger.TrigSetup.ClearOneShot();
            }

            fdataReader.WriteToLevel(lvl);

            //Save it and read it back in
            writer.WriteLevelToFile(lvl, "TEST.tr2");
            lvl = reader.ReadLevel("TEST.tr2");

            fdataReader = new FDControl();
            fdataReader.ParseFromLevel(lvl);

            //Get the triggers again afresh
            triggers = FDUtilities.GetEntityTriggers(fdataReader, 18);

            //Verify that they now once again do not have OneShot set
            foreach (FDTriggerEntry trigger in triggers)
            {
                Assert.IsFalse(trigger.TrigSetup.OneShot);
            }
        }
Пример #12
0
        public void WriteToLevel(TR2Level lvl)
        {
            List <ushort> data = new List <ushort>
            {
                lvl.FloorData[0] // Index 0 is always dummy
            };

            Dictionary <int, int> entryLengths = new Dictionary <int, int>();

            foreach (KeyValuePair <int, List <FDEntry> > entry in Entries)
            {
                //Get the list of functions per sector
                List <FDEntry> functions = entry.Value;

                //Get the initial sector index into fdata
                int index = entry.Key;
                //Track the total length of the entry
                int entryLength = 0;

                for (int i = 0; i < functions.Count; i++)
                {
                    FDEntry function = functions[i];

                    // Ensure EndData is set on the last function in the list only
                    function.Setup.EndData = i == functions.Count - 1;

                    //Convert function to ushort array
                    ushort[] fdata = function.Flatten();
                    data.AddRange(fdata);

                    //Store how many shorts there are
                    entryLength += fdata.Length;
                }

                // Map the current index to its FD length
                entryLengths.Add(index, entryLength);
            }

            // Recalculate the indices based on the length of the floor data for each.
            // This allows for new entries to be added (or their entries added to) or removed.
            Dictionary <int, int> newIndices = new Dictionary <int, int>();
            int newIndex = 1; // Always start at 1 as 0 is dummy data

            foreach (int index in entryLengths.Keys)
            {
                newIndices.Add(index, newIndex);
                // The next index will be this index plus the number of ushorts against it in the FD
                newIndex += entryLengths[index];
            }

            // Update each TRRoomSector by repointing its FDIndex value to the newly calculated value.
            SortedDictionary <int, List <FDEntry> > _updatedEntries = new SortedDictionary <int, List <FDEntry> >();

            foreach (TR2Room room in lvl.Rooms)
            {
                foreach (TRRoomSector sector in room.SectorList)
                {
                    ushort index = sector.FDIndex;
                    if (newIndices.ContainsKey(index))
                    {
                        sector.FDIndex = (ushort)newIndices[index];

                        // Map the list of entries against the new index
                        _updatedEntries.Add(sector.FDIndex, _entries[index]);
                    }
                }
            }

            // Update the raw floor data in the level
            lvl.FloorData    = data.ToArray();
            lvl.NumFloorData = (uint)data.Count;

            // Update the stored values in case of further changes
            _entries = _updatedEntries;
        }
Пример #13
0
 public static TRMesh GetModelFirstMesh(TR2Level level, TRModel model)
 {
     return(GetMesh(level, model.StartingMesh));
 }
Пример #14
0
        // Given a precompiled dictionary of old texture index to new, this will ensure that
        // all Meshes, RoomData and AnimatedTextures point to the new correct index.
        public static void ReindexTextures(this TR2Level level, Dictionary <int, int> indexMap, bool defaultToOriginal = true)
        {
            if (indexMap.Count == 0)
            {
                return;
            }

            foreach (TRMesh mesh in level.Meshes)
            {
                foreach (TRFace4 rect in mesh.TexturedRectangles)
                {
                    rect.Texture = ConvertTextureReference(rect.Texture, indexMap, defaultToOriginal);
                }
                foreach (TRFace3 tri in mesh.TexturedTriangles)
                {
                    tri.Texture = ConvertTextureReference(tri.Texture, indexMap, defaultToOriginal);
                }
            }

            foreach (TR2Room room in level.Rooms)
            {
                foreach (TRFace4 rect in room.RoomData.Rectangles)
                {
                    rect.Texture = ConvertTextureReference(rect.Texture, indexMap, defaultToOriginal);
                }
                foreach (TRFace3 tri in room.RoomData.Triangles)
                {
                    tri.Texture = ConvertTextureReference(tri.Texture, indexMap, defaultToOriginal);
                }
            }

            // #137 Ensure animated textures are reindexed too (these are just groups of texture indices)
            // They have to remain unique it seems, otherwise the animation speed is too fast, so while we
            // have removed the duplicated textures, we can re-add duplicate texture objects while there is
            // enough space in that array.
            List <TRObjectTexture> textures = level.ObjectTextures.ToList();

            foreach (TRAnimatedTexture anim in level.AnimatedTextures)
            {
                for (int i = 0; i < anim.Textures.Length; i++)
                {
                    anim.Textures[i] = ConvertTextureReference(anim.Textures[i], indexMap, defaultToOriginal);
                }

                ushort previousIndex = anim.Textures[0];
                for (int i = 1; i < anim.Textures.Length; i++)
                {
                    if (anim.Textures[i] == previousIndex && textures.Count < 2048)
                    {
                        textures.Add(textures[anim.Textures[i]]);
                        anim.Textures[i] = (ushort)(textures.Count - 1);
                    }
                    previousIndex = anim.Textures[i];
                }
            }

            if (textures.Count > level.NumObjectTextures)
            {
                level.ObjectTextures    = textures.ToArray();
                level.NumObjectTextures = (uint)textures.Count;
            }
        }
Пример #15
0
        public TR2Level ReadLevel(string Filename)
        {
            if (!Filename.ToUpper().Contains("TR2"))
            {
                throw new NotImplementedException("File reader only supports TR2 levels");
            }

            TR2Level level = new TR2Level();

            reader = new BinaryReader(File.Open(Filename, FileMode.Open));

            //Version
            level.Version = reader.ReadUInt32();
            if (level.Version != TR2VersionHeader)
            {
                throw new NotImplementedException("File reader only suppors TR2 levels");
            }

            //Colour palettes and textures
            level.Palette = PopulateColourPalette(reader.ReadBytes((int)MAX_PALETTE_SIZE * 3));

            level.Palette16 = PopulateColourPalette16(reader.ReadBytes((int)MAX_PALETTE_SIZE * 4));

            level.NumImages = reader.ReadUInt32();

            level.Images8  = new TRTexImage8[level.NumImages];
            level.Images16 = new TRTexImage16[level.NumImages];

            //Initialize the texture arrays
            for (int i = 0; i < level.NumImages; i++)
            {
                level.Images8[i]  = new TRTexImage8();
                level.Images16[i] = new TRTexImage16();
            }

            //For each texture8 there are 256 * 256 bytes (65536) we can just do a straight byte read
            for (int i = 0; i < level.NumImages; i++)
            {
                level.Images8[i].Pixels = reader.ReadBytes(256 * 256);
            }

            //For each texture16 there are 256 * 256 * 2 bytes (131072)
            for (int i = 0; i < level.NumImages; i++)
            {
                level.Images16[i].Pixels = new ushort[256 * 256];

                for (int j = 0; j < level.Images16[i].Pixels.Count(); j++)
                {
                    level.Images16[i].Pixels[j] = reader.ReadUInt16();
                }
            }

            //Rooms
            level.Unused   = reader.ReadUInt32();
            level.NumRooms = reader.ReadUInt16();
            level.Rooms    = new TR2Room[level.NumRooms];

            for (int i = 0; i < level.NumRooms; i++)
            {
                TR2Room room = new TR2Room();

                //Grab info
                room.Info = new TRRoomInfo
                {
                    X       = reader.ReadInt32(),
                    Z       = reader.ReadInt32(),
                    YBottom = reader.ReadInt32(),
                    YTop    = reader.ReadInt32()
                };

                //Grab data
                room.NumDataWords = reader.ReadUInt32();
                room.Data         = new ushort[room.NumDataWords];
                for (int j = 0; j < room.NumDataWords; j++)
                {
                    room.Data[j] = reader.ReadUInt16();
                }

                //Store what we just read
                room.RoomData = ConvertToRoomData(room);

                //Portals
                room.NumPortals = reader.ReadUInt16();
                room.Portals    = new TRRoomPortal[room.NumPortals];
                for (int j = 0; j < room.NumPortals; j++)
                {
                    room.Portals[j] = TR2FileReadUtilities.ReadRoomPortal(reader);
                }

                //Sectors
                room.NumZSectors = reader.ReadUInt16();
                room.NumXSectors = reader.ReadUInt16();
                room.SectorList  = new TRRoomSector[room.NumXSectors * room.NumZSectors];
                for (int j = 0; j < (room.NumXSectors * room.NumZSectors); j++)
                {
                    room.SectorList[j] = TR2FileReadUtilities.ReadRoomSector(reader);
                }

                //Lighting
                room.AmbientIntensity  = reader.ReadInt16();
                room.AmbientIntensity2 = reader.ReadInt16();
                room.LightMode         = reader.ReadInt16();
                room.NumLights         = reader.ReadUInt16();
                room.Lights            = new TR2RoomLight[room.NumLights];
                for (int j = 0; j < room.NumLights; j++)
                {
                    room.Lights[j] = TR2FileReadUtilities.ReadRoomLight(reader);
                }

                //Static meshes
                room.NumStaticMeshes = reader.ReadUInt16();
                room.StaticMeshes    = new TR2RoomStaticMesh[room.NumStaticMeshes];
                for (int j = 0; j < room.NumStaticMeshes; j++)
                {
                    room.StaticMeshes[j] = TR2FileReadUtilities.ReadRoomStaticMesh(reader);
                }

                room.AlternateRoom = reader.ReadInt16();
                room.Flags         = reader.ReadInt16();

                level.Rooms[i] = room;
            }

            //Floordata
            level.NumFloorData = reader.ReadUInt32();
            level.FloorData    = new ushort[level.NumFloorData];

            for (int i = 0; i < level.NumFloorData; i++)
            {
                level.FloorData[i] = reader.ReadUInt16();
            }

            //Mesh Data
            //This tells us how much mesh data (# of words/uint16s) coming up
            //just like the rooms previously.
            level.NumMeshData = reader.ReadUInt32();
            level.RawMeshData = new ushort[level.NumMeshData];

            for (int i = 0; i < level.NumMeshData; i++)
            {
                level.RawMeshData[i] = reader.ReadUInt16();
            }

            //Mesh Pointers
            level.NumMeshPointers = reader.ReadUInt32();
            level.MeshPointers    = new uint[level.NumMeshPointers];

            for (int i = 0; i < level.NumMeshPointers; i++)
            {
                level.MeshPointers[i] = reader.ReadUInt32();
            }

            //Mesh Construction
            //level.Meshes = ConstructMeshData(level.NumMeshData, level.NumMeshPointers, level.RawMeshData);

            //Animations
            level.NumAnimations = reader.ReadUInt32();
            level.Animations    = new TRAnimation[level.NumAnimations];
            for (int i = 0; i < level.NumAnimations; i++)
            {
                level.Animations[i] = TR2FileReadUtilities.ReadAnimation(reader);
            }

            //State Changes
            level.NumStateChanges = reader.ReadUInt32();
            level.StateChanges    = new TRStateChange[level.NumStateChanges];
            for (int i = 0; i < level.NumStateChanges; i++)
            {
                level.StateChanges[i] = TR2FileReadUtilities.ReadStateChange(reader);
            }

            //Animation Dispatches
            level.NumAnimDispatches = reader.ReadUInt32();
            level.AnimDispatches    = new TRAnimDispatch[level.NumAnimDispatches];
            for (int i = 0; i < level.NumAnimDispatches; i++)
            {
                level.AnimDispatches[i] = TR2FileReadUtilities.ReadAnimDispatch(reader);
            }

            //Animation Commands
            level.NumAnimCommands = reader.ReadUInt32();
            level.AnimCommands    = new TRAnimCommand[level.NumAnimCommands];
            for (int i = 0; i < level.NumAnimCommands; i++)
            {
                level.AnimCommands[i] = TR2FileReadUtilities.ReadAnimCommand(reader);
            }

            //Mesh Trees
            level.NumMeshTrees  = reader.ReadUInt32();
            level.NumMeshTrees /= 4;
            level.MeshTrees     = new TRMeshTreeNode[level.NumMeshTrees];
            for (int i = 0; i < level.NumMeshTrees; i++)
            {
                level.MeshTrees[i] = TR2FileReadUtilities.ReadMeshTreeNode(reader);
            }

            //Frames
            level.NumFrames = reader.ReadUInt32();
            level.Frames    = new ushort[level.NumFrames];
            for (int i = 0; i < level.NumFrames; i++)
            {
                level.Frames[i] = reader.ReadUInt16();
            }

            //Models
            level.NumModels = reader.ReadUInt32();
            level.Models    = new TRModel[level.NumModels];

            for (int i = 0; i < level.NumModels; i++)
            {
                level.Models[i] = TR2FileReadUtilities.ReadModel(reader);
            }

            //Static Meshes
            level.NumStaticMeshes = reader.ReadUInt32();
            level.StaticMeshes    = new TRStaticMesh[level.NumStaticMeshes];

            for (int i = 0; i < level.NumStaticMeshes; i++)
            {
                level.StaticMeshes[i] = TR2FileReadUtilities.ReadStaticMesh(reader);
            }

            //Object Textures
            level.NumObjectTextures = reader.ReadUInt32();
            level.ObjectTextures    = new TRObjectTexture[level.NumObjectTextures];

            for (int i = 0; i < level.NumObjectTextures; i++)
            {
                level.ObjectTextures[i] = TR2FileReadUtilities.ReadObjectTexture(reader);
            }

            //Sprite Textures
            level.NumSpriteTextures = reader.ReadUInt32();
            level.SpriteTextures    = new TRSpriteTexture[level.NumSpriteTextures];

            for (int i = 0; i < level.NumSpriteTextures; i++)
            {
                level.SpriteTextures[i] = TR2FileReadUtilities.ReadSpriteTexture(reader);
            }

            //Sprite Sequences
            level.NumSpriteSequences = reader.ReadUInt32();
            level.SpriteSequences    = new TRSpriteSequence[level.NumSpriteSequences];

            for (int i = 0; i < level.NumSpriteSequences; i++)
            {
                level.SpriteSequences[i] = TR2FileReadUtilities.ReadSpriteSequence(reader);
            }

            //Cameras
            level.NumCameras = reader.ReadUInt32();
            level.Cameras    = new TRCamera[level.NumCameras];

            for (int i = 0; i < level.NumCameras; i++)
            {
                level.Cameras[i] = TR2FileReadUtilities.ReadCamera(reader);
            }

            //Sound Sources
            level.NumSoundSources = reader.ReadUInt32();
            level.SoundSources    = new TRSoundSource[level.NumSoundSources];

            for (int i = 0; i < level.NumSoundSources; i++)
            {
                level.SoundSources[i] = TR2FileReadUtilities.ReadSoundSource(reader);
            }

            //Boxes
            level.NumBoxes = reader.ReadUInt32();
            level.Boxes    = new TR2Box[level.NumBoxes];

            for (int i = 0; i < level.NumBoxes; i++)
            {
                level.Boxes[i] = TR2FileReadUtilities.ReadBox(reader);
            }

            //Overlaps & Zones
            level.NumOverlaps = reader.ReadUInt32();
            level.Overlaps    = new ushort[level.NumOverlaps];
            level.Zones       = new short[10 * level.NumBoxes];

            for (int i = 0; i < level.NumOverlaps; i++)
            {
                level.Overlaps[i] = reader.ReadUInt16();
            }

            for (int i = 0; i < level.Zones.Count(); i++)
            {
                level.Zones[i] = reader.ReadInt16();
            }

            //Animated Textures
            level.NumAnimatedTextures = reader.ReadUInt32();
            level.AnimatedTextures    = new ushort[level.NumAnimatedTextures];

            for (int i = 0; i < level.NumAnimatedTextures; i++)
            {
                level.AnimatedTextures[i] = reader.ReadUInt16();
            }

            //Entities
            level.NumEntities = reader.ReadUInt32();
            level.Entities    = new TR2Entity[level.NumEntities];

            for (int i = 0; i < level.NumEntities; i++)
            {
                level.Entities[i] = TR2FileReadUtilities.ReadEntity(reader);
            }

            //Light Map - 32 * 256 = 8192 bytes
            level.LightMap = new byte[32 * 256];

            for (int i = 0; i < level.LightMap.Count(); i++)
            {
                level.LightMap[i] = reader.ReadByte();
            }

            //Cinematic Frames
            level.NumCinematicFrames = reader.ReadUInt16();
            level.CinematicFrames    = new TRCinematicFrame[level.NumCinematicFrames];

            for (int i = 0; i < level.NumCinematicFrames; i++)
            {
                level.CinematicFrames[i] = TR2FileReadUtilities.ReadCinematicFrame(reader);
            }

            //Demo Data
            level.NumDemoData = reader.ReadUInt16();
            level.DemoData    = new byte[level.NumDemoData];

            for (int i = 0; i < level.NumDemoData; i++)
            {
                level.DemoData[i] = reader.ReadByte();
            }

            //Sound Map (370 shorts = 740 bytes) & Sound Details
            level.SoundMap = new short[370];

            for (int i = 0; i < level.SoundMap.Count(); i++)
            {
                level.SoundMap[i] = reader.ReadInt16();
            }

            level.NumSoundDetails = reader.ReadUInt32();
            level.SoundDetails    = new TRSoundDetails[level.NumSoundDetails];

            for (int i = 0; i < level.NumSoundDetails; i++)
            {
                level.SoundDetails[i] = TR2FileReadUtilities.ReadSoundDetails(reader);
            }

            //Samples
            level.NumSampleIndices = reader.ReadUInt32();
            level.SampleIndices    = new uint[level.NumSampleIndices];

            for (int i = 0; i < level.NumSampleIndices; i++)
            {
                level.SampleIndices[i] = reader.ReadUInt32();
            }

            Debug.Assert(reader.BaseStream.Position == reader.BaseStream.Length);

            reader.Close();

            return(level);
        }
Пример #16
0
 public HtmlTileBuilder(TR2Level level)
 {
     _level = level;
 }
Пример #17
0
 public static int GetFreeObjectTextureCount(this TR2Level level)
 {
     return(2048 - (int)level.NumObjectTextures + level.GetInvalidObjectTextureIndices().Count);
 }
Пример #18
0
        public static TextureLevelMapping Get(TR2Level level, string mappingFilePrefix, TextureDatabase database, Dictionary <StaticTextureSource, List <StaticTextureTarget> > predefinedMapping = null, List <TR2Entities> entitiesToIgnore = null)
        {
            string mapFile = Path.Combine(@"Resources\Textures\Mapping\", mappingFilePrefix + "-Textures.json");

            if (!File.Exists(mapFile))
            {
                return(null);
            }

            Dictionary <DynamicTextureSource, DynamicTextureTarget>       dynamicMapping = new Dictionary <DynamicTextureSource, DynamicTextureTarget>();
            Dictionary <StaticTextureSource, List <StaticTextureTarget> > staticMapping  = new Dictionary <StaticTextureSource, List <StaticTextureTarget> >();
            Dictionary <StaticTextureSource, Dictionary <int, List <LandmarkTextureTarget> > > landmarkMapping = new Dictionary <StaticTextureSource, Dictionary <int, List <LandmarkTextureTarget> > >();
            Color skyBoxColour = _defaultSkyBox;

            Dictionary <string, object> rootMapping = JsonConvert.DeserializeObject <Dictionary <string, object> >(File.ReadAllText(mapFile));

            // Read the dynamic mapping - this holds object and sprite texture indices for the level to which we will apply an HSB operation
            if (rootMapping.ContainsKey("Dynamic"))
            {
                SortedDictionary <string, Dictionary <string, object> > mapping = JsonConvert.DeserializeObject <SortedDictionary <string, Dictionary <string, object> > >(rootMapping["Dynamic"].ToString());
                foreach (string sourceName in mapping.Keys)
                {
                    DynamicTextureSource source = database.GetDynamicSource(sourceName);
                    DynamicTextureTarget target = new DynamicTextureTarget
                    {
                        DefaultTileTargets = JsonConvert.DeserializeObject <Dictionary <int, List <Rectangle> > >(mapping[sourceName]["Default"].ToString())
                    };

                    if (mapping[sourceName].ContainsKey("Optional"))
                    {
                        target.OptionalTileTargets = JsonConvert.DeserializeObject <Dictionary <TextureCategory, Dictionary <int, List <Rectangle> > > >(mapping[sourceName]["Optional"].ToString());
                    }

                    dynamicMapping[source] = target;
                }
            }

            // The static mapping contains basic texture segment source to tile target locations
            if (rootMapping.ContainsKey("Static"))
            {
                SortedDictionary <string, object> mapping = JsonConvert.DeserializeObject <SortedDictionary <string, object> >(rootMapping["Static"].ToString());
                foreach (string sourceName in mapping.Keys)
                {
                    staticMapping[database.GetStaticSource(sourceName)] = JsonConvert.DeserializeObject <List <StaticTextureTarget> >(mapping[sourceName].ToString());
                }
            }

            // Landmark mapping links static sources to room number -> rectangle/triangle indices
            if (rootMapping.ContainsKey("Landmarks"))
            {
                Dictionary <string, Dictionary <int, List <LandmarkTextureTarget> > > mapping = JsonConvert.DeserializeObject <Dictionary <string, Dictionary <int, List <LandmarkTextureTarget> > > >(rootMapping["Landmarks"].ToString());
                foreach (string sourceName in mapping.Keys)
                {
                    landmarkMapping[database.GetStaticSource(sourceName)] = mapping[sourceName];
                }
            }

            // If a level has had textures removed externally, but the JSON file has static
            // imports ready for it, we need to make sure they are ignored.
            if (entitiesToIgnore != null)
            {
                List <StaticTextureSource> sources = new List <StaticTextureSource>(staticMapping.Keys);
                for (int i = 0; i < sources.Count; i++)
                {
                    StaticTextureSource source = sources[i];
                    if (source.TextureEntities != null)
                    {
                        foreach (TR2Entities entity in source.TextureEntities)
                        {
                            if (entitiesToIgnore.Contains(entity))
                            {
                                staticMapping.Remove(source);
                                break;
                            }
                        }
                    }
                }
            }

            // Allows for dynamic mapping to be targeted at levels e.g. when importing non-native
            // models that are otherwise undefined in the default level JSON data.
            // This should be done after removing ignored entity textures, for the likes of when
            // Lara is being replaced.
            if (predefinedMapping != null)
            {
                foreach (StaticTextureSource source in predefinedMapping.Keys)
                {
                    staticMapping[source] = predefinedMapping[source];
                }
            }

            // Add global sources, unless they are already defined. These tend to be sprite sequences
            // so they will be mapped per GenerateSpriteSequenceTargets, but there is also scope to
            // define global targets if relevant.
            foreach (StaticTextureSource source in database.GlobalGrouping.Sources.Keys)
            {
                if (!staticMapping.ContainsKey(source))
                {
                    staticMapping[source] = new List <StaticTextureTarget>(database.GlobalGrouping.Sources[source]);
                }
            }

            // Apply grouping to what has been selected as source elements
            List <TextureGrouping> staticGrouping = database.GlobalGrouping.GetGrouping(staticMapping.Keys);

            return(new TextureLevelMapping(level)
            {
                DynamicMapping = dynamicMapping,
                StaticMapping = staticMapping,
                StaticGrouping = staticGrouping,
                LandmarkMapping = landmarkMapping,
                DefaultSkyBox = skyBoxColour
            });
        }
Пример #19
0
 private TextureLevelMapping(TR2Level level)
 {
     _level     = level;
     _tileMap   = new Dictionary <int, BitmapGraphics>();
     _committed = false;
 }
Пример #20
0
        public void SaveLevel(TR2Level level, string name)
        {
            string fullPath = Path.Combine(BasePath, name);

            _writer.WriteLevelToFile(level, fullPath);
        }
Пример #21
0
 protected void LoadLevelInstance(TR23ScriptedLevel scriptedLevel)
 {
     _scriptedLevelInstance = scriptedLevel;
     _levelInstance         = LoadLevel(scriptedLevel.LevelFileBaseName);
 }
Пример #22
0
        public void ParseFromLevel(TR2Level lvl)
        {
            _entries = new SortedDictionary <int, List <FDEntry> >();

            foreach (TR2Room room in lvl.Rooms)
            {
                foreach (TRRoomSector sector in room.SectorList)
                {
                    //Index into FData is FDIndex
                    ushort index = sector.FDIndex;

                    //Index 0 is a dummy
                    if (index == 0)
                    {
                        continue;
                    }

                    //List of floordata functions for the sector
                    List <FDEntry> floordataFunctions = new List <FDEntry>();

                    while (true)
                    {
                        FDSetup data = new FDSetup()
                        {
                            Value = lvl.FloorData[index]
                        };

                        switch ((FDFunctions)data.Function)
                        {
                        case FDFunctions.PortalSector:

                            FDPortalEntry portal = new FDPortalEntry()
                            {
                                Setup = new FDSetup()
                                {
                                    Value = lvl.FloorData[index]
                                },
                                Room = lvl.FloorData[++index]
                            };

                            floordataFunctions.Add(portal);

                            break;

                        case FDFunctions.FloorSlant:

                            FDSlantEntry floorSlant = new FDSlantEntry()
                            {
                                Setup = new FDSetup()
                                {
                                    Value = lvl.FloorData[index]
                                },
                                SlantValue = lvl.FloorData[++index],
                                Type       = FDSlantEntryType.FloorSlant
                            };

                            floordataFunctions.Add(floorSlant);

                            break;

                        case FDFunctions.CeilingSlant:

                            FDSlantEntry ceilingSlant = new FDSlantEntry()
                            {
                                Setup = new FDSetup()
                                {
                                    Value = lvl.FloorData[index]
                                },
                                SlantValue = lvl.FloorData[++index],
                                Type       = FDSlantEntryType.CeilingSlant
                            };

                            floordataFunctions.Add(ceilingSlant);

                            break;

                        case FDFunctions.Trigger:

                            FDTriggerEntry trig = new FDTriggerEntry()
                            {
                                Setup = new FDSetup()
                                {
                                    Value = lvl.FloorData[index]
                                },
                                TrigSetup = new FDTrigSetup()
                                {
                                    Value = lvl.FloorData[++index]
                                }
                            };

                            if (trig.TrigType == FDTrigType.Switch || trig.TrigType == FDTrigType.Key)
                            {
                                //First entry in action list is reference to switch/key entity for switch/key types.
                                trig.SwitchOrKeyRef = lvl.FloorData[++index];
                            }

                            //We don't know if there are any more yet.
                            bool continueFDParse;

                            //Parse trigactions
                            do
                            {
                                //New trigger action
                                FDActionListItem action = new FDActionListItem()
                                {
                                    Value = lvl.FloorData[++index]
                                };

                                continueFDParse = action.Continue;

                                if (action.TrigAction == FDTrigAction.Camera)
                                {
                                    //Camera trig actions have a special extra uint16...
                                    FDCameraAction camAction = new FDCameraAction()
                                    {
                                        Value = lvl.FloorData[++index]
                                    };

                                    //store associated camera action
                                    action.CamAction = camAction;

                                    //Is there more?
                                    continueFDParse = camAction.Continue;
                                }

                                //add action
                                trig.TrigActionList.Add(action);
                            } while (index < lvl.NumFloorData && continueFDParse);

                            floordataFunctions.Add(trig);

                            break;

                        case FDFunctions.KillLara:

                            FDKillLaraEntry kill = new FDKillLaraEntry()
                            {
                                Setup = new FDSetup()
                                {
                                    Value = lvl.FloorData[index]
                                }
                            };

                            floordataFunctions.Add(kill);

                            break;

                        case FDFunctions.ClimbableWalls:

                            FDClimbEntry climb = new FDClimbEntry()
                            {
                                Setup = new FDSetup()
                                {
                                    Value = lvl.FloorData[index]
                                }
                            };

                            floordataFunctions.Add(climb);

                            break;

                        case FDFunctions.FloorTriangulationNWSE_Solid:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.FloorTriangulationNESW_Solid:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.CeilingTriangulationNW_Solid:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.CeilingTriangulationNE_Solid:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.FloorTriangulationNWSE_SW:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.FloorTriangulationNWSE_NE:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.FloorTriangulationNESW_SW:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.FloorTriangulationNESW_NW:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.CeilingTriangulationNW_SW:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.CeilingTriangulationNW_NE:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.CeilingTriangulationNE_NW:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.CeilingTriangulationNE_SE:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.Monkeyswing:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.DeferredTriggeringOrMinecartRotateLeft:
                            //TR3 Only - Ignore for now...
                            break;

                        case FDFunctions.MechBeetleOrMinecartRotateRight:
                            //TR3 Only - Ignore for now...
                            break;

                        default:
                            break;
                        }

                        if (data.EndData)
                        {
                            //End data (from what I understand) means there is no further functions for this sector.
                            //E.G. Sector 52 on Xian has a slant function and portal function. EndData is not set on
                            //slant function, but is on portal function as there are no further functions.
                            break;
                        }
                        else
                        {
                            //There are further functions for this sector - continue parsing.
                            index++;
                        }
                    }

                    //Store the sector index and all of its associated functions
                    _entries.Add(sector.FDIndex, floordataFunctions);
                }
            }
        }